@elizaos/app-core 2.0.0-alpha.37
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/.turbo/turbo-build.log +2 -0
- package/LICENSE +21 -0
- package/dist/App.d.ts +5 -0
- package/dist/App.d.ts.map +1 -0
- package/dist/App.js +198 -0
- package/dist/actions/character.d.ts +27 -0
- package/dist/actions/character.d.ts.map +1 -0
- package/dist/actions/character.js +97 -0
- package/dist/actions/chat-helpers.d.ts +47 -0
- package/dist/actions/chat-helpers.d.ts.map +1 -0
- package/dist/actions/chat-helpers.js +79 -0
- package/dist/actions/cloud.d.ts +17 -0
- package/dist/actions/cloud.d.ts.map +1 -0
- package/dist/actions/cloud.js +43 -0
- package/dist/actions/index.d.ts +12 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +11 -0
- package/dist/actions/lifecycle.d.ts +43 -0
- package/dist/actions/lifecycle.d.ts.map +1 -0
- package/dist/actions/lifecycle.js +118 -0
- package/dist/actions/onboarding.d.ts +12 -0
- package/dist/actions/onboarding.d.ts.map +1 -0
- package/dist/actions/onboarding.js +28 -0
- package/dist/actions/triggers.d.ts +23 -0
- package/dist/actions/triggers.d.ts.map +1 -0
- package/dist/actions/triggers.js +148 -0
- package/dist/api/client.d.ts +2823 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +2392 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +1 -0
- package/dist/autonomy/index.d.ts +48 -0
- package/dist/autonomy/index.d.ts.map +1 -0
- package/dist/autonomy/index.js +330 -0
- package/dist/bridge/capacitor-bridge.d.ts +153 -0
- package/dist/bridge/capacitor-bridge.d.ts.map +1 -0
- package/dist/bridge/capacitor-bridge.js +193 -0
- package/dist/bridge/electrobun-rpc.d.ts +19 -0
- package/dist/bridge/electrobun-rpc.d.ts.map +1 -0
- package/dist/bridge/electrobun-rpc.js +27 -0
- package/dist/bridge/electrobun-runtime.d.ts +3 -0
- package/dist/bridge/electrobun-runtime.d.ts.map +1 -0
- package/dist/bridge/electrobun-runtime.js +17 -0
- package/dist/bridge/index.d.ts +6 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +5 -0
- package/dist/bridge/native-plugins.d.ts +82 -0
- package/dist/bridge/native-plugins.d.ts.map +1 -0
- package/dist/bridge/native-plugins.js +39 -0
- package/dist/bridge/plugin-bridge.d.ts +116 -0
- package/dist/bridge/plugin-bridge.d.ts.map +1 -0
- package/dist/bridge/plugin-bridge.js +203 -0
- package/dist/bridge/storage-bridge.d.ts +39 -0
- package/dist/bridge/storage-bridge.d.ts.map +1 -0
- package/dist/bridge/storage-bridge.js +135 -0
- package/dist/chat/index.d.ts +57 -0
- package/dist/chat/index.d.ts.map +1 -0
- package/dist/chat/index.js +161 -0
- package/dist/coding/index.d.ts +25 -0
- package/dist/coding/index.d.ts.map +1 -0
- package/dist/coding/index.js +25 -0
- package/dist/components/AdvancedPageView.d.ts +17 -0
- package/dist/components/AdvancedPageView.d.ts.map +1 -0
- package/dist/components/AdvancedPageView.js +137 -0
- package/dist/components/AgentActivityBox.d.ts +7 -0
- package/dist/components/AgentActivityBox.d.ts.map +1 -0
- package/dist/components/AgentActivityBox.js +25 -0
- package/dist/components/ApiKeyConfig.d.ts +26 -0
- package/dist/components/ApiKeyConfig.d.ts.map +1 -0
- package/dist/components/ApiKeyConfig.js +121 -0
- package/dist/components/AppsPageView.d.ts +7 -0
- package/dist/components/AppsPageView.d.ts.map +1 -0
- package/dist/components/AppsPageView.js +31 -0
- package/dist/components/AppsView.d.ts +8 -0
- package/dist/components/AppsView.d.ts.map +1 -0
- package/dist/components/AppsView.js +149 -0
- package/dist/components/AvatarLoader.d.ts +9 -0
- package/dist/components/AvatarLoader.d.ts.map +1 -0
- package/dist/components/AvatarLoader.js +45 -0
- package/dist/components/AvatarSelector.d.ts +23 -0
- package/dist/components/AvatarSelector.d.ts.map +1 -0
- package/dist/components/AvatarSelector.js +105 -0
- package/dist/components/BscTradePanel.d.ts +22 -0
- package/dist/components/BscTradePanel.d.ts.map +1 -0
- package/dist/components/BscTradePanel.js +221 -0
- package/dist/components/BugReportModal.d.ts +2 -0
- package/dist/components/BugReportModal.d.ts.map +1 -0
- package/dist/components/BugReportModal.js +218 -0
- package/dist/components/CharacterView.d.ts +8 -0
- package/dist/components/CharacterView.d.ts.map +1 -0
- package/dist/components/CharacterView.js +703 -0
- package/dist/components/ChatAvatar.d.ts +8 -0
- package/dist/components/ChatAvatar.d.ts.map +1 -0
- package/dist/components/ChatAvatar.js +89 -0
- package/dist/components/ChatComposer.d.ts +37 -0
- package/dist/components/ChatComposer.d.ts.map +1 -0
- package/dist/components/ChatComposer.js +136 -0
- package/dist/components/ChatMessage.d.ts +24 -0
- package/dist/components/ChatMessage.d.ts.map +1 -0
- package/dist/components/ChatMessage.js +167 -0
- package/dist/components/ChatModalView.d.ts +10 -0
- package/dist/components/ChatModalView.d.ts.map +1 -0
- package/dist/components/ChatModalView.js +57 -0
- package/dist/components/ChatView.d.ts +14 -0
- package/dist/components/ChatView.d.ts.map +1 -0
- package/dist/components/ChatView.js +511 -0
- package/dist/components/CloudSourceControls.d.ts +13 -0
- package/dist/components/CloudSourceControls.d.ts.map +1 -0
- package/dist/components/CloudSourceControls.js +14 -0
- package/dist/components/CodingAgentSettingsSection.d.ts +2 -0
- package/dist/components/CodingAgentSettingsSection.d.ts.map +1 -0
- package/dist/components/CodingAgentSettingsSection.js +268 -0
- package/dist/components/CommandPalette.d.ts +2 -0
- package/dist/components/CommandPalette.d.ts.map +1 -0
- package/dist/components/CommandPalette.js +181 -0
- package/dist/components/CompanionSceneHost.d.ts +15 -0
- package/dist/components/CompanionSceneHost.d.ts.map +1 -0
- package/dist/components/CompanionSceneHost.js +343 -0
- package/dist/components/CompanionShell.d.ts +17 -0
- package/dist/components/CompanionShell.d.ts.map +1 -0
- package/dist/components/CompanionShell.js +15 -0
- package/dist/components/CompanionView.d.ts +2 -0
- package/dist/components/CompanionView.d.ts.map +1 -0
- package/dist/components/CompanionView.js +22 -0
- package/dist/components/ConfigPageView.d.ts +11 -0
- package/dist/components/ConfigPageView.d.ts.map +1 -0
- package/dist/components/ConfigPageView.js +275 -0
- package/dist/components/ConfigSaveFooter.d.ts +8 -0
- package/dist/components/ConfigSaveFooter.d.ts.map +1 -0
- package/dist/components/ConfigSaveFooter.js +10 -0
- package/dist/components/ConfirmModal.d.ts +61 -0
- package/dist/components/ConfirmModal.d.ts.map +1 -0
- package/dist/components/ConfirmModal.js +164 -0
- package/dist/components/ConnectionFailedBanner.d.ts +6 -0
- package/dist/components/ConnectionFailedBanner.d.ts.map +1 -0
- package/dist/components/ConnectionFailedBanner.js +22 -0
- package/dist/components/ConnectorsPageView.d.ts +7 -0
- package/dist/components/ConnectorsPageView.d.ts.map +1 -0
- package/dist/components/ConnectorsPageView.js +8 -0
- package/dist/components/ConversationsSidebar.d.ts +9 -0
- package/dist/components/ConversationsSidebar.d.ts.map +1 -0
- package/dist/components/ConversationsSidebar.js +116 -0
- package/dist/components/CustomActionEditor.d.ts +10 -0
- package/dist/components/CustomActionEditor.d.ts.map +1 -0
- package/dist/components/CustomActionEditor.js +578 -0
- package/dist/components/CustomActionsPanel.d.ts +9 -0
- package/dist/components/CustomActionsPanel.d.ts.map +1 -0
- package/dist/components/CustomActionsPanel.js +107 -0
- package/dist/components/CustomActionsView.d.ts +2 -0
- package/dist/components/CustomActionsView.d.ts.map +1 -0
- package/dist/components/CustomActionsView.js +134 -0
- package/dist/components/DatabasePageView.d.ts +5 -0
- package/dist/components/DatabasePageView.d.ts.map +1 -0
- package/dist/components/DatabasePageView.js +28 -0
- package/dist/components/DatabaseView.d.ts +9 -0
- package/dist/components/DatabaseView.d.ts.map +1 -0
- package/dist/components/DatabaseView.js +311 -0
- package/dist/components/ElizaCloudDashboard.d.ts +2 -0
- package/dist/components/ElizaCloudDashboard.d.ts.map +1 -0
- package/dist/components/ElizaCloudDashboard.js +657 -0
- package/dist/components/EmotePicker.d.ts +2 -0
- package/dist/components/EmotePicker.d.ts.map +1 -0
- package/dist/components/EmotePicker.js +343 -0
- package/dist/components/ErrorBoundary.d.ts +22 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.js +31 -0
- package/dist/components/FineTuningView.d.ts +2 -0
- package/dist/components/FineTuningView.d.ts.map +1 -0
- package/dist/components/FineTuningView.js +433 -0
- package/dist/components/GameView.d.ts +11 -0
- package/dist/components/GameView.d.ts.map +1 -0
- package/dist/components/GameView.js +295 -0
- package/dist/components/GameViewOverlay.d.ts +8 -0
- package/dist/components/GameViewOverlay.d.ts.map +1 -0
- package/dist/components/GameViewOverlay.js +70 -0
- package/dist/components/GlobalEmoteOverlay.d.ts +2 -0
- package/dist/components/GlobalEmoteOverlay.d.ts.map +1 -0
- package/dist/components/GlobalEmoteOverlay.js +112 -0
- package/dist/components/Header.d.ts +8 -0
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.js +121 -0
- package/dist/components/HeartbeatsView.d.ts +2 -0
- package/dist/components/HeartbeatsView.d.ts.map +1 -0
- package/dist/components/HeartbeatsView.js +378 -0
- package/dist/components/InventoryView.d.ts +10 -0
- package/dist/components/InventoryView.d.ts.map +1 -0
- package/dist/components/InventoryView.js +162 -0
- package/dist/components/KnowledgeView.d.ts +20 -0
- package/dist/components/KnowledgeView.d.ts.map +1 -0
- package/dist/components/KnowledgeView.js +480 -0
- package/dist/components/LanguageDropdown.d.ts +30 -0
- package/dist/components/LanguageDropdown.d.ts.map +1 -0
- package/dist/components/LanguageDropdown.js +98 -0
- package/dist/components/LifoMonitorPanel.d.ts +21 -0
- package/dist/components/LifoMonitorPanel.d.ts.map +1 -0
- package/dist/components/LifoMonitorPanel.js +24 -0
- package/dist/components/LifoSandboxView.d.ts +5 -0
- package/dist/components/LifoSandboxView.d.ts.map +1 -0
- package/dist/components/LifoSandboxView.js +333 -0
- package/dist/components/LoadingScreen.d.ts +13 -0
- package/dist/components/LoadingScreen.d.ts.map +1 -0
- package/dist/components/LoadingScreen.js +39 -0
- package/dist/components/LogsPageView.d.ts +2 -0
- package/dist/components/LogsPageView.d.ts.map +1 -0
- package/dist/components/LogsPageView.js +7 -0
- package/dist/components/LogsView.d.ts +5 -0
- package/dist/components/LogsView.d.ts.map +1 -0
- package/dist/components/LogsView.js +71 -0
- package/dist/components/MediaGalleryView.d.ts +9 -0
- package/dist/components/MediaGalleryView.d.ts.map +1 -0
- package/dist/components/MediaGalleryView.js +236 -0
- package/dist/components/MediaSettingsSection.d.ts +11 -0
- package/dist/components/MediaSettingsSection.d.ts.map +1 -0
- package/dist/components/MediaSettingsSection.js +329 -0
- package/dist/components/MessageContent.d.ts +51 -0
- package/dist/components/MessageContent.d.ts.map +1 -0
- package/dist/components/MessageContent.js +553 -0
- package/dist/components/OnboardingWizard.d.ts +2 -0
- package/dist/components/OnboardingWizard.d.ts.map +1 -0
- package/dist/components/OnboardingWizard.js +59 -0
- package/dist/components/PairingView.d.ts +5 -0
- package/dist/components/PairingView.d.ts.map +1 -0
- package/dist/components/PairingView.js +28 -0
- package/dist/components/PermissionsSection.d.ts +20 -0
- package/dist/components/PermissionsSection.d.ts.map +1 -0
- package/dist/components/PermissionsSection.js +368 -0
- package/dist/components/PluginsPageView.d.ts +5 -0
- package/dist/components/PluginsPageView.d.ts.map +1 -0
- package/dist/components/PluginsPageView.js +8 -0
- package/dist/components/PluginsView.d.ts +21 -0
- package/dist/components/PluginsView.d.ts.map +1 -0
- package/dist/components/PluginsView.js +1531 -0
- package/dist/components/ProviderSwitcher.d.ts +42 -0
- package/dist/components/ProviderSwitcher.d.ts.map +1 -0
- package/dist/components/ProviderSwitcher.js +480 -0
- package/dist/components/RestartBanner.d.ts +2 -0
- package/dist/components/RestartBanner.d.ts.map +1 -0
- package/dist/components/RestartBanner.js +36 -0
- package/dist/components/RuntimeView.d.ts +10 -0
- package/dist/components/RuntimeView.d.ts.map +1 -0
- package/dist/components/RuntimeView.js +165 -0
- package/dist/components/SaveCommandModal.d.ts +12 -0
- package/dist/components/SaveCommandModal.d.ts.map +1 -0
- package/dist/components/SaveCommandModal.js +84 -0
- package/dist/components/SecretsView.d.ts +9 -0
- package/dist/components/SecretsView.d.ts.map +1 -0
- package/dist/components/SecretsView.js +249 -0
- package/dist/components/SettingsView.d.ts +9 -0
- package/dist/components/SettingsView.d.ts.map +1 -0
- package/dist/components/SettingsView.js +230 -0
- package/dist/components/ShellOverlays.d.ts +8 -0
- package/dist/components/ShellOverlays.d.ts.map +1 -0
- package/dist/components/ShellOverlays.js +10 -0
- package/dist/components/ShortcutsOverlay.d.ts +2 -0
- package/dist/components/ShortcutsOverlay.d.ts.map +1 -0
- package/dist/components/ShortcutsOverlay.js +79 -0
- package/dist/components/SkillsView.d.ts +11 -0
- package/dist/components/SkillsView.d.ts.map +1 -0
- package/dist/components/SkillsView.js +358 -0
- package/dist/components/StartupFailureView.d.ts +8 -0
- package/dist/components/StartupFailureView.d.ts.map +1 -0
- package/dist/components/StartupFailureView.js +15 -0
- package/dist/components/StreamView.d.ts +16 -0
- package/dist/components/StreamView.d.ts.map +1 -0
- package/dist/components/StreamView.js +300 -0
- package/dist/components/StripeEmbeddedCheckout.d.ts +24 -0
- package/dist/components/StripeEmbeddedCheckout.d.ts.map +1 -0
- package/dist/components/StripeEmbeddedCheckout.js +101 -0
- package/dist/components/SubscriptionStatus.d.ts +22 -0
- package/dist/components/SubscriptionStatus.d.ts.map +1 -0
- package/dist/components/SubscriptionStatus.js +301 -0
- package/dist/components/SystemWarningBanner.d.ts +6 -0
- package/dist/components/SystemWarningBanner.d.ts.map +1 -0
- package/dist/components/SystemWarningBanner.js +46 -0
- package/dist/components/ThemeToggle.d.ts +21 -0
- package/dist/components/ThemeToggle.d.ts.map +1 -0
- package/dist/components/ThemeToggle.js +24 -0
- package/dist/components/TrajectoriesView.d.ts +12 -0
- package/dist/components/TrajectoriesView.d.ts.map +1 -0
- package/dist/components/TrajectoriesView.js +183 -0
- package/dist/components/TrajectoryDetailView.d.ts +13 -0
- package/dist/components/TrajectoryDetailView.d.ts.map +1 -0
- package/dist/components/TrajectoryDetailView.js +112 -0
- package/dist/components/TriggersView.d.ts +2 -0
- package/dist/components/TriggersView.d.ts.map +1 -0
- package/dist/components/TriggersView.js +1 -0
- package/dist/components/VectorBrowserView.d.ts +10 -0
- package/dist/components/VectorBrowserView.d.ts.map +1 -0
- package/dist/components/VectorBrowserView.js +997 -0
- package/dist/components/VoiceConfigView.d.ts +11 -0
- package/dist/components/VoiceConfigView.d.ts.map +1 -0
- package/dist/components/VoiceConfigView.js +329 -0
- package/dist/components/VrmStage.d.ts +21 -0
- package/dist/components/VrmStage.d.ts.map +1 -0
- package/dist/components/VrmStage.js +252 -0
- package/dist/components/WhatsAppQrOverlay.d.ts +8 -0
- package/dist/components/WhatsAppQrOverlay.d.ts.map +1 -0
- package/dist/components/WhatsAppQrOverlay.js +63 -0
- package/dist/components/apps/AppDetailPane.d.ts +15 -0
- package/dist/components/apps/AppDetailPane.d.ts.map +1 -0
- package/dist/components/apps/AppDetailPane.js +13 -0
- package/dist/components/apps/AppsCatalogGrid.d.ts +20 -0
- package/dist/components/apps/AppsCatalogGrid.d.ts.map +1 -0
- package/dist/components/apps/AppsCatalogGrid.js +18 -0
- package/dist/components/apps/extensions/HyperscapeAppDetailPanel.d.ts +3 -0
- package/dist/components/apps/extensions/HyperscapeAppDetailPanel.d.ts.map +1 -0
- package/dist/components/apps/extensions/HyperscapeAppDetailPanel.js +253 -0
- package/dist/components/apps/extensions/registry.d.ts +4 -0
- package/dist/components/apps/extensions/registry.d.ts.map +1 -0
- package/dist/components/apps/extensions/registry.js +10 -0
- package/dist/components/apps/extensions/types.d.ts +7 -0
- package/dist/components/apps/extensions/types.d.ts.map +1 -0
- package/dist/components/apps/extensions/types.js +1 -0
- package/dist/components/apps/helpers.d.ts +7 -0
- package/dist/components/apps/helpers.d.ts.map +1 -0
- package/dist/components/apps/helpers.js +46 -0
- package/dist/components/avatar/VrmAnimationLoader.d.ts +30 -0
- package/dist/components/avatar/VrmAnimationLoader.d.ts.map +1 -0
- package/dist/components/avatar/VrmAnimationLoader.js +99 -0
- package/dist/components/avatar/VrmBlinkController.d.ts +37 -0
- package/dist/components/avatar/VrmBlinkController.d.ts.map +1 -0
- package/dist/components/avatar/VrmBlinkController.js +98 -0
- package/dist/components/avatar/VrmCameraManager.d.ts +57 -0
- package/dist/components/avatar/VrmCameraManager.d.ts.map +1 -0
- package/dist/components/avatar/VrmCameraManager.js +277 -0
- package/dist/components/avatar/VrmEngine.d.ts +229 -0
- package/dist/components/avatar/VrmEngine.d.ts.map +1 -0
- package/dist/components/avatar/VrmEngine.js +1950 -0
- package/dist/components/avatar/VrmFootShadow.d.ts +18 -0
- package/dist/components/avatar/VrmFootShadow.d.ts.map +1 -0
- package/dist/components/avatar/VrmFootShadow.js +83 -0
- package/dist/components/avatar/VrmViewer.d.ts +45 -0
- package/dist/components/avatar/VrmViewer.d.ts.map +1 -0
- package/dist/components/avatar/VrmViewer.js +341 -0
- package/dist/components/avatar/mixamoVRMRigMap.d.ts +3 -0
- package/dist/components/avatar/mixamoVRMRigMap.d.ts.map +1 -0
- package/dist/components/avatar/mixamoVRMRigMap.js +56 -0
- package/dist/components/avatar/retargetMixamoFbxToVrm.d.ts +9 -0
- package/dist/components/avatar/retargetMixamoFbxToVrm.d.ts.map +1 -0
- package/dist/components/avatar/retargetMixamoFbxToVrm.js +88 -0
- package/dist/components/avatar/retargetMixamoGltfToVrm.d.ts +11 -0
- package/dist/components/avatar/retargetMixamoGltfToVrm.d.ts.map +1 -0
- package/dist/components/avatar/retargetMixamoGltfToVrm.js +80 -0
- package/dist/components/chainConfig.d.ts +84 -0
- package/dist/components/chainConfig.d.ts.map +1 -0
- package/dist/components/chainConfig.js +268 -0
- package/dist/components/companion/CompanionHeader.d.ts +15 -0
- package/dist/components/companion/CompanionHeader.d.ts.map +1 -0
- package/dist/components/companion/CompanionHeader.js +7 -0
- package/dist/components/companion/CompanionSceneHost.d.ts +2 -0
- package/dist/components/companion/CompanionSceneHost.d.ts.map +1 -0
- package/dist/components/companion/CompanionSceneHost.js +1 -0
- package/dist/components/companion/VrmStage.d.ts +3 -0
- package/dist/components/companion/VrmStage.d.ts.map +1 -0
- package/dist/components/companion/VrmStage.js +1 -0
- package/dist/components/companion/walletUtils.d.ts +95 -0
- package/dist/components/companion/walletUtils.d.ts.map +1 -0
- package/dist/components/companion/walletUtils.js +167 -0
- package/dist/components/companion-shell-styles.d.ts +38 -0
- package/dist/components/companion-shell-styles.d.ts.map +1 -0
- package/dist/components/companion-shell-styles.js +248 -0
- package/dist/components/confirm-delete-control.d.ts +16 -0
- package/dist/components/confirm-delete-control.d.ts.map +1 -0
- package/dist/components/confirm-delete-control.js +12 -0
- package/dist/components/conversations/ConversationListItem.d.ts +31 -0
- package/dist/components/conversations/ConversationListItem.d.ts.map +1 -0
- package/dist/components/conversations/ConversationListItem.js +52 -0
- package/dist/components/conversations/conversation-utils.d.ts +9 -0
- package/dist/components/conversations/conversation-utils.d.ts.map +1 -0
- package/dist/components/conversations/conversation-utils.js +138 -0
- package/dist/components/format.d.ts +54 -0
- package/dist/components/format.d.ts.map +1 -0
- package/dist/components/format.js +82 -0
- package/dist/components/index.d.ts +93 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +92 -0
- package/dist/components/inventory/CopyableAddress.d.ts +8 -0
- package/dist/components/inventory/CopyableAddress.d.ts.map +1 -0
- package/dist/components/inventory/CopyableAddress.js +18 -0
- package/dist/components/inventory/InventoryToolbar.d.ts +25 -0
- package/dist/components/inventory/InventoryToolbar.d.ts.map +1 -0
- package/dist/components/inventory/InventoryToolbar.js +28 -0
- package/dist/components/inventory/NftGrid.d.ts +13 -0
- package/dist/components/inventory/NftGrid.d.ts.map +1 -0
- package/dist/components/inventory/NftGrid.js +29 -0
- package/dist/components/inventory/TokenLogo.d.ts +12 -0
- package/dist/components/inventory/TokenLogo.d.ts.map +1 -0
- package/dist/components/inventory/TokenLogo.js +33 -0
- package/dist/components/inventory/TokensTable.d.ts +24 -0
- package/dist/components/inventory/TokensTable.d.ts.map +1 -0
- package/dist/components/inventory/TokensTable.js +26 -0
- package/dist/components/inventory/constants.d.ts +52 -0
- package/dist/components/inventory/constants.d.ts.map +1 -0
- package/dist/components/inventory/constants.js +121 -0
- package/dist/components/inventory/index.d.ts +9 -0
- package/dist/components/inventory/index.d.ts.map +1 -0
- package/dist/components/inventory/index.js +8 -0
- package/dist/components/inventory/media-url.d.ts +6 -0
- package/dist/components/inventory/media-url.d.ts.map +1 -0
- package/dist/components/inventory/media-url.js +28 -0
- package/dist/components/inventory/useInventoryData.d.ts +53 -0
- package/dist/components/inventory/useInventoryData.d.ts.map +1 -0
- package/dist/components/inventory/useInventoryData.js +332 -0
- package/dist/components/knowledge-upload-image.d.ts +27 -0
- package/dist/components/knowledge-upload-image.d.ts.map +1 -0
- package/dist/components/knowledge-upload-image.js +146 -0
- package/dist/components/labels.d.ts +6 -0
- package/dist/components/labels.d.ts.map +1 -0
- package/dist/components/labels.js +40 -0
- package/dist/components/onboarding/ActivateStep.d.ts +2 -0
- package/dist/components/onboarding/ActivateStep.d.ts.map +1 -0
- package/dist/components/onboarding/ActivateStep.js +6 -0
- package/dist/components/onboarding/ConnectionStep.d.ts +2 -0
- package/dist/components/onboarding/ConnectionStep.d.ts.map +1 -0
- package/dist/components/onboarding/ConnectionStep.js +552 -0
- package/dist/components/onboarding/OnboardingPanel.d.ts +9 -0
- package/dist/components/onboarding/OnboardingPanel.d.ts.map +1 -0
- package/dist/components/onboarding/OnboardingPanel.js +24 -0
- package/dist/components/onboarding/OnboardingStepNav.d.ts +2 -0
- package/dist/components/onboarding/OnboardingStepNav.d.ts.map +1 -0
- package/dist/components/onboarding/OnboardingStepNav.js +14 -0
- package/dist/components/onboarding/PermissionsStep.d.ts +2 -0
- package/dist/components/onboarding/PermissionsStep.d.ts.map +1 -0
- package/dist/components/onboarding/PermissionsStep.js +7 -0
- package/dist/components/onboarding/RpcStep.d.ts +2 -0
- package/dist/components/onboarding/RpcStep.d.ts.map +1 -0
- package/dist/components/onboarding/RpcStep.js +125 -0
- package/dist/components/onboarding/WakeUpStep.d.ts +2 -0
- package/dist/components/onboarding/WakeUpStep.d.ts.map +1 -0
- package/dist/components/onboarding/WakeUpStep.js +82 -0
- package/dist/components/permissions/PermissionIcon.d.ts +4 -0
- package/dist/components/permissions/PermissionIcon.d.ts.map +1 -0
- package/dist/components/permissions/PermissionIcon.js +12 -0
- package/dist/components/permissions/StreamingPermissions.d.ts +20 -0
- package/dist/components/permissions/StreamingPermissions.d.ts.map +1 -0
- package/dist/components/permissions/StreamingPermissions.js +173 -0
- package/dist/components/plugins/showcase-data.d.ts +7 -0
- package/dist/components/plugins/showcase-data.d.ts.map +1 -0
- package/dist/components/plugins/showcase-data.js +464 -0
- package/dist/components/shared/ShellHeaderControls.d.ts +27 -0
- package/dist/components/shared/ShellHeaderControls.d.ts.map +1 -0
- package/dist/components/shared/ShellHeaderControls.js +60 -0
- package/dist/components/skeletons.d.ts +17 -0
- package/dist/components/skeletons.d.ts.map +1 -0
- package/dist/components/skeletons.js +22 -0
- package/dist/components/stream/ActivityFeed.d.ts +5 -0
- package/dist/components/stream/ActivityFeed.d.ts.map +1 -0
- package/dist/components/stream/ActivityFeed.js +57 -0
- package/dist/components/stream/AvatarPip.d.ts +5 -0
- package/dist/components/stream/AvatarPip.d.ts.map +1 -0
- package/dist/components/stream/AvatarPip.js +6 -0
- package/dist/components/stream/ChatContent.d.ts +6 -0
- package/dist/components/stream/ChatContent.d.ts.map +1 -0
- package/dist/components/stream/ChatContent.js +69 -0
- package/dist/components/stream/ChatTicker.d.ts +5 -0
- package/dist/components/stream/ChatTicker.d.ts.map +1 -0
- package/dist/components/stream/ChatTicker.js +34 -0
- package/dist/components/stream/IdleContent.d.ts +5 -0
- package/dist/components/stream/IdleContent.d.ts.map +1 -0
- package/dist/components/stream/IdleContent.js +17 -0
- package/dist/components/stream/StatusBar.d.ts +36 -0
- package/dist/components/stream/StatusBar.d.ts.map +1 -0
- package/dist/components/stream/StatusBar.js +140 -0
- package/dist/components/stream/StreamSettings.d.ts +33 -0
- package/dist/components/stream/StreamSettings.d.ts.map +1 -0
- package/dist/components/stream/StreamSettings.js +99 -0
- package/dist/components/stream/StreamTerminal.d.ts +2 -0
- package/dist/components/stream/StreamTerminal.d.ts.map +1 -0
- package/dist/components/stream/StreamTerminal.js +52 -0
- package/dist/components/stream/StreamVoiceConfig.d.ts +10 -0
- package/dist/components/stream/StreamVoiceConfig.d.ts.map +1 -0
- package/dist/components/stream/StreamVoiceConfig.js +88 -0
- package/dist/components/stream/helpers.d.ts +32 -0
- package/dist/components/stream/helpers.d.ts.map +1 -0
- package/dist/components/stream/helpers.js +110 -0
- package/dist/components/stream/overlays/OverlayLayer.d.ts +20 -0
- package/dist/components/stream/overlays/OverlayLayer.d.ts.map +1 -0
- package/dist/components/stream/overlays/OverlayLayer.js +24 -0
- package/dist/components/stream/overlays/built-in/ActionTickerWidget.d.ts +8 -0
- package/dist/components/stream/overlays/built-in/ActionTickerWidget.d.ts.map +1 -0
- package/dist/components/stream/overlays/built-in/ActionTickerWidget.js +44 -0
- package/dist/components/stream/overlays/built-in/AlertPopupWidget.d.ts +7 -0
- package/dist/components/stream/overlays/built-in/AlertPopupWidget.d.ts.map +1 -0
- package/dist/components/stream/overlays/built-in/AlertPopupWidget.js +62 -0
- package/dist/components/stream/overlays/built-in/BrandingWidget.d.ts +7 -0
- package/dist/components/stream/overlays/built-in/BrandingWidget.d.ts.map +1 -0
- package/dist/components/stream/overlays/built-in/BrandingWidget.js +36 -0
- package/dist/components/stream/overlays/built-in/CustomHtmlWidget.d.ts +26 -0
- package/dist/components/stream/overlays/built-in/CustomHtmlWidget.d.ts.map +1 -0
- package/dist/components/stream/overlays/built-in/CustomHtmlWidget.js +78 -0
- package/dist/components/stream/overlays/built-in/PeonGlassWidget.d.ts +11 -0
- package/dist/components/stream/overlays/built-in/PeonGlassWidget.d.ts.map +1 -0
- package/dist/components/stream/overlays/built-in/PeonGlassWidget.js +188 -0
- package/dist/components/stream/overlays/built-in/PeonHudWidget.d.ts +10 -0
- package/dist/components/stream/overlays/built-in/PeonHudWidget.d.ts.map +1 -0
- package/dist/components/stream/overlays/built-in/PeonHudWidget.js +168 -0
- package/dist/components/stream/overlays/built-in/PeonSakuraWidget.d.ts +11 -0
- package/dist/components/stream/overlays/built-in/PeonSakuraWidget.d.ts.map +1 -0
- package/dist/components/stream/overlays/built-in/PeonSakuraWidget.js +213 -0
- package/dist/components/stream/overlays/built-in/ThoughtBubbleWidget.d.ts +8 -0
- package/dist/components/stream/overlays/built-in/ThoughtBubbleWidget.d.ts.map +1 -0
- package/dist/components/stream/overlays/built-in/ThoughtBubbleWidget.js +59 -0
- package/dist/components/stream/overlays/built-in/ViewerCountWidget.d.ts +7 -0
- package/dist/components/stream/overlays/built-in/ViewerCountWidget.d.ts.map +1 -0
- package/dist/components/stream/overlays/built-in/ViewerCountWidget.js +34 -0
- package/dist/components/stream/overlays/built-in/index.d.ts +13 -0
- package/dist/components/stream/overlays/built-in/index.d.ts.map +1 -0
- package/dist/components/stream/overlays/built-in/index.js +12 -0
- package/dist/components/stream/overlays/registry.d.ts +11 -0
- package/dist/components/stream/overlays/registry.d.ts.map +1 -0
- package/dist/components/stream/overlays/registry.js +16 -0
- package/dist/components/stream/overlays/types.d.ts +67 -0
- package/dist/components/stream/overlays/types.d.ts.map +1 -0
- package/dist/components/stream/overlays/types.js +7 -0
- package/dist/components/stream/overlays/useOverlayLayout.d.ts +27 -0
- package/dist/components/stream/overlays/useOverlayLayout.d.ts.map +1 -0
- package/dist/components/stream/overlays/useOverlayLayout.js +162 -0
- package/dist/components/trajectory-format.d.ts +6 -0
- package/dist/components/trajectory-format.d.ts.map +1 -0
- package/dist/components/trajectory-format.js +43 -0
- package/dist/components/ui-badges.d.ts +23 -0
- package/dist/components/ui-badges.d.ts.map +1 -0
- package/dist/components/ui-badges.js +38 -0
- package/dist/components/ui-switch.d.ts +14 -0
- package/dist/components/ui-switch.d.ts.map +1 -0
- package/dist/components/ui-switch.js +15 -0
- package/dist/components/vector-browser-three.d.ts +4 -0
- package/dist/components/vector-browser-three.d.ts.map +1 -0
- package/dist/components/vector-browser-three.js +19 -0
- package/dist/config/config-catalog.d.ts +376 -0
- package/dist/config/config-catalog.d.ts.map +1 -0
- package/dist/config/config-catalog.js +724 -0
- package/dist/config/config-field.d.ts +68 -0
- package/dist/config/config-field.d.ts.map +1 -0
- package/dist/config/config-field.js +821 -0
- package/dist/config/config-renderer.d.ts +176 -0
- package/dist/config/config-renderer.d.ts.map +1 -0
- package/dist/config/config-renderer.js +403 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/ui-renderer.d.ts +26 -0
- package/dist/config/ui-renderer.d.ts.map +1 -0
- package/dist/config/ui-renderer.js +815 -0
- package/dist/config/ui-spec.d.ts +164 -0
- package/dist/config/ui-spec.d.ts.map +1 -0
- package/dist/config/ui-spec.js +13 -0
- package/dist/events/index.d.ts +42 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +41 -0
- package/dist/hooks/index.d.ts +14 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +13 -0
- package/dist/hooks/useBugReport.d.ts +14 -0
- package/dist/hooks/useBugReport.d.ts.map +1 -0
- package/dist/hooks/useBugReport.js +18 -0
- package/dist/hooks/useCanvasWindow.d.ts +38 -0
- package/dist/hooks/useCanvasWindow.d.ts.map +1 -0
- package/dist/hooks/useCanvasWindow.js +273 -0
- package/dist/hooks/useChatAvatarVoice.d.ts +10 -0
- package/dist/hooks/useChatAvatarVoice.d.ts.map +1 -0
- package/dist/hooks/useChatAvatarVoice.js +71 -0
- package/dist/hooks/useContextMenu.d.ts +17 -0
- package/dist/hooks/useContextMenu.d.ts.map +1 -0
- package/dist/hooks/useContextMenu.js +100 -0
- package/dist/hooks/useKeyboardShortcuts.d.ts +17 -0
- package/dist/hooks/useKeyboardShortcuts.d.ts.map +1 -0
- package/dist/hooks/useKeyboardShortcuts.js +67 -0
- package/dist/hooks/useLifoSync.d.ts +18 -0
- package/dist/hooks/useLifoSync.d.ts.map +1 -0
- package/dist/hooks/useLifoSync.js +113 -0
- package/dist/hooks/useMemoryMonitor.d.ts +87 -0
- package/dist/hooks/useMemoryMonitor.d.ts.map +1 -0
- package/dist/hooks/useMemoryMonitor.js +210 -0
- package/dist/hooks/useRenderGuard.d.ts +17 -0
- package/dist/hooks/useRenderGuard.d.ts.map +1 -0
- package/dist/hooks/useRenderGuard.js +36 -0
- package/dist/hooks/useRetakeCapture.d.ts +12 -0
- package/dist/hooks/useRetakeCapture.d.ts.map +1 -0
- package/dist/hooks/useRetakeCapture.js +59 -0
- package/dist/hooks/useStreamPopoutNavigation.d.ts +3 -0
- package/dist/hooks/useStreamPopoutNavigation.d.ts.map +1 -0
- package/dist/hooks/useStreamPopoutNavigation.js +17 -0
- package/dist/hooks/useTimeout.d.ts +11 -0
- package/dist/hooks/useTimeout.d.ts.map +1 -0
- package/dist/hooks/useTimeout.js +32 -0
- package/dist/hooks/useVoiceChat.d.ts +129 -0
- package/dist/hooks/useVoiceChat.d.ts.map +1 -0
- package/dist/hooks/useVoiceChat.js +1071 -0
- package/dist/hooks/useWhatsAppPairing.d.ts +11 -0
- package/dist/hooks/useWhatsAppPairing.d.ts.map +1 -0
- package/dist/hooks/useWhatsAppPairing.js +95 -0
- package/dist/i18n/index.d.ts +7 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +53 -0
- package/dist/i18n/locales/en.json +1196 -0
- package/dist/i18n/locales/es.json +1196 -0
- package/dist/i18n/locales/ko.json +1196 -0
- package/dist/i18n/locales/pt.json +1196 -0
- package/dist/i18n/locales/zh-CN.json +1196 -0
- package/dist/i18n/messages.d.ts +6 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +14 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/navigation/index.d.ts +27 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +230 -0
- package/dist/package.json +161 -0
- package/dist/platform/browser-launch.d.ts +2 -0
- package/dist/platform/browser-launch.d.ts.map +1 -0
- package/dist/platform/browser-launch.js +109 -0
- package/dist/platform/index.d.ts +64 -0
- package/dist/platform/index.d.ts.map +1 -0
- package/dist/platform/index.js +155 -0
- package/dist/platform/init.d.ts +40 -0
- package/dist/platform/init.d.ts.map +1 -0
- package/dist/platform/init.js +158 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +72 -0
- package/dist/state/AppContext.d.ts +11 -0
- package/dist/state/AppContext.d.ts.map +1 -0
- package/dist/state/AppContext.js +4496 -0
- package/dist/state/index.d.ts +7 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +6 -0
- package/dist/state/internal.d.ts +6 -0
- package/dist/state/internal.d.ts.map +1 -0
- package/dist/state/internal.js +5 -0
- package/dist/state/parsers.d.ts +26 -0
- package/dist/state/parsers.d.ts.map +1 -0
- package/dist/state/parsers.js +255 -0
- package/dist/state/persistence.d.ts +36 -0
- package/dist/state/persistence.d.ts.map +1 -0
- package/dist/state/persistence.js +254 -0
- package/dist/state/types.d.ts +402 -0
- package/dist/state/types.d.ts.map +1 -0
- package/dist/state/types.js +70 -0
- package/dist/state/ui-preferences.d.ts +3 -0
- package/dist/state/ui-preferences.d.ts.map +1 -0
- package/dist/state/ui-preferences.js +1 -0
- package/dist/state/useApp.d.ts +4 -0
- package/dist/state/useApp.d.ts.map +1 -0
- package/dist/state/useApp.js +22 -0
- package/dist/state/vrm.d.ts +19 -0
- package/dist/state/vrm.d.ts.map +1 -0
- package/dist/state/vrm.js +65 -0
- package/dist/stories/AppMockProvider.d.ts +15 -0
- package/dist/stories/AppMockProvider.d.ts.map +1 -0
- package/dist/stories/AppMockProvider.js +14 -0
- package/dist/styles/anime.css +6324 -0
- package/dist/styles/base.css +196 -0
- package/dist/styles/onboarding-game.css +716 -0
- package/dist/styles/styles.css +2085 -0
- package/dist/styles/xterm.css +241 -0
- package/dist/types/index.d.ts +657 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/asset-url.d.ts +26 -0
- package/dist/utils/asset-url.d.ts.map +1 -0
- package/dist/utils/asset-url.js +99 -0
- package/dist/utils/assistant-text.d.ts +2 -0
- package/dist/utils/assistant-text.d.ts.map +1 -0
- package/dist/utils/assistant-text.js +161 -0
- package/dist/utils/clipboard.d.ts +2 -0
- package/dist/utils/clipboard.d.ts.map +1 -0
- package/dist/utils/clipboard.js +38 -0
- package/dist/utils/desktop-dialogs.d.ts +19 -0
- package/dist/utils/desktop-dialogs.d.ts.map +1 -0
- package/dist/utils/desktop-dialogs.js +50 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/number-parsing.d.ts +44 -0
- package/dist/utils/number-parsing.d.ts.map +1 -0
- package/dist/utils/number-parsing.js +56 -0
- package/dist/utils/openExternalUrl.d.ts +2 -0
- package/dist/utils/openExternalUrl.d.ts.map +1 -0
- package/dist/utils/openExternalUrl.js +17 -0
- package/dist/utils/spoken-text.d.ts +2 -0
- package/dist/utils/spoken-text.d.ts.map +1 -0
- package/dist/utils/spoken-text.js +56 -0
- package/dist/utils/streaming-text.d.ts +3 -0
- package/dist/utils/streaming-text.d.ts.map +1 -0
- package/dist/utils/streaming-text.js +87 -0
- package/dist/voice/index.d.ts +2 -0
- package/dist/voice/index.d.ts.map +1 -0
- package/dist/voice/index.js +1 -0
- package/dist/voice/types.d.ts +25 -0
- package/dist/voice/types.d.ts.map +1 -0
- package/dist/voice/types.js +166 -0
- package/package.json +86 -0
- package/src/App.tsx +469 -0
- package/src/actions/character.ts +113 -0
- package/src/actions/chat-helpers.ts +100 -0
- package/src/actions/cloud.ts +59 -0
- package/src/actions/index.ts +12 -0
- package/src/actions/lifecycle.ts +175 -0
- package/src/actions/onboarding.ts +46 -0
- package/src/actions/triggers.ts +190 -0
- package/src/ambient.d.ts +16 -0
- package/src/api/client.ts +5616 -0
- package/src/api/index.ts +1 -0
- package/src/autonomy/index.ts +477 -0
- package/src/bridge/capacitor-bridge.ts +295 -0
- package/src/bridge/electrobun-rpc.ts +58 -0
- package/src/bridge/electrobun-runtime.ts +28 -0
- package/src/bridge/index.ts +5 -0
- package/src/bridge/native-plugins.ts +134 -0
- package/src/bridge/plugin-bridge.ts +352 -0
- package/src/bridge/storage-bridge.ts +162 -0
- package/src/chat/index.ts +251 -0
- package/src/coding/index.ts +43 -0
- package/src/components/AdvancedPageView.tsx +362 -0
- package/src/components/AgentActivityBox.tsx +49 -0
- package/src/components/ApiKeyConfig.tsx +224 -0
- package/src/components/AppsPageView.tsx +52 -0
- package/src/components/AppsView.tsx +293 -0
- package/src/components/AvatarLoader.tsx +86 -0
- package/src/components/AvatarSelector.tsx +223 -0
- package/src/components/BscTradePanel.tsx +549 -0
- package/src/components/BugReportModal.tsx +499 -0
- package/src/components/CharacterView.tsx +1623 -0
- package/src/components/ChatAvatar.test.ts +96 -0
- package/src/components/ChatAvatar.tsx +147 -0
- package/src/components/ChatComposer.tsx +330 -0
- package/src/components/ChatMessage.tsx +448 -0
- package/src/components/ChatModalView.test.tsx +118 -0
- package/src/components/ChatModalView.tsx +125 -0
- package/src/components/ChatView.tsx +999 -0
- package/src/components/CloudSourceControls.tsx +80 -0
- package/src/components/CodingAgentSettingsSection.tsx +536 -0
- package/src/components/CommandPalette.tsx +284 -0
- package/src/components/CompanionSceneHost.tsx +498 -0
- package/src/components/CompanionShell.tsx +31 -0
- package/src/components/CompanionView.tsx +109 -0
- package/src/components/ConfigPageView.tsx +722 -0
- package/src/components/ConfigSaveFooter.tsx +41 -0
- package/src/components/ConfirmModal.tsx +379 -0
- package/src/components/ConnectionFailedBanner.tsx +91 -0
- package/src/components/ConnectorsPageView.tsx +13 -0
- package/src/components/ConversationsSidebar.tsx +279 -0
- package/src/components/CustomActionEditor.tsx +1125 -0
- package/src/components/CustomActionsPanel.tsx +288 -0
- package/src/components/CustomActionsView.tsx +322 -0
- package/src/components/DatabasePageView.tsx +55 -0
- package/src/components/DatabaseView.tsx +814 -0
- package/src/components/ElizaCloudDashboard.tsx +1696 -0
- package/src/components/EmotePicker.tsx +529 -0
- package/src/components/ErrorBoundary.tsx +76 -0
- package/src/components/FineTuningView.tsx +1080 -0
- package/src/components/GameView.tsx +551 -0
- package/src/components/GameViewOverlay.tsx +133 -0
- package/src/components/GlobalEmoteOverlay.tsx +152 -0
- package/src/components/Header.test.tsx +413 -0
- package/src/components/Header.tsx +400 -0
- package/src/components/HeartbeatsView.tsx +1002 -0
- package/src/components/InventoryView.tsx +372 -0
- package/src/components/KnowledgeView.tsx +1128 -0
- package/src/components/LanguageDropdown.tsx +187 -0
- package/src/components/LifoMonitorPanel.tsx +196 -0
- package/src/components/LifoSandboxView.tsx +499 -0
- package/src/components/LoadingScreen.tsx +77 -0
- package/src/components/LogsPageView.tsx +17 -0
- package/src/components/LogsView.tsx +239 -0
- package/src/components/MediaGalleryView.tsx +432 -0
- package/src/components/MediaSettingsSection.tsx +893 -0
- package/src/components/MessageContent.tsx +815 -0
- package/src/components/OnboardingWizard.test.tsx +107 -0
- package/src/components/OnboardingWizard.tsx +186 -0
- package/src/components/PairingView.tsx +110 -0
- package/src/components/PermissionsSection.tsx +818 -0
- package/src/components/PluginsPageView.tsx +9 -0
- package/src/components/PluginsView.tsx +3152 -0
- package/src/components/ProviderSwitcher.tsx +874 -0
- package/src/components/RestartBanner.tsx +76 -0
- package/src/components/RuntimeView.tsx +460 -0
- package/src/components/SaveCommandModal.tsx +211 -0
- package/src/components/SecretsView.tsx +569 -0
- package/src/components/SettingsView.tsx +825 -0
- package/src/components/ShellOverlays.tsx +41 -0
- package/src/components/ShortcutsOverlay.tsx +155 -0
- package/src/components/SkillsView.tsx +1435 -0
- package/src/components/StartupFailureView.tsx +63 -0
- package/src/components/StreamView.tsx +483 -0
- package/src/components/StripeEmbeddedCheckout.tsx +155 -0
- package/src/components/SubscriptionStatus.tsx +634 -0
- package/src/components/SystemWarningBanner.tsx +71 -0
- package/src/components/ThemeToggle.tsx +100 -0
- package/src/components/TrajectoriesView.tsx +526 -0
- package/src/components/TrajectoryDetailView.tsx +426 -0
- package/src/components/TriggersView.tsx +1 -0
- package/src/components/VectorBrowserView.tsx +1633 -0
- package/src/components/VoiceConfigView.tsx +674 -0
- package/src/components/VrmStage.test.ts +219 -0
- package/src/components/VrmStage.tsx +432 -0
- package/src/components/WhatsAppQrOverlay.tsx +230 -0
- package/src/components/__tests__/chainConfig.test.ts +220 -0
- package/src/components/apps/AppDetailPane.tsx +242 -0
- package/src/components/apps/AppsCatalogGrid.tsx +137 -0
- package/src/components/apps/extensions/HyperscapeAppDetailPanel.tsx +577 -0
- package/src/components/apps/extensions/registry.ts +16 -0
- package/src/components/apps/extensions/types.ts +9 -0
- package/src/components/apps/helpers.ts +44 -0
- package/src/components/avatar/VrmAnimationLoader.test.ts +164 -0
- package/src/components/avatar/VrmAnimationLoader.ts +151 -0
- package/src/components/avatar/VrmBlinkController.ts +118 -0
- package/src/components/avatar/VrmCameraManager.ts +407 -0
- package/src/components/avatar/VrmEngine.ts +2678 -0
- package/src/components/avatar/VrmFootShadow.ts +96 -0
- package/src/components/avatar/VrmViewer.tsx +421 -0
- package/src/components/avatar/__tests__/VrmCameraManager.test.ts +168 -0
- package/src/components/avatar/__tests__/VrmEngine.test.ts +1574 -0
- package/src/components/avatar/mixamoVRMRigMap.ts +62 -0
- package/src/components/avatar/retargetMixamoFbxToVrm.ts +144 -0
- package/src/components/avatar/retargetMixamoGltfToVrm.ts +119 -0
- package/src/components/chainConfig.ts +380 -0
- package/src/components/companion/CompanionHeader.tsx +47 -0
- package/src/components/companion/CompanionSceneHost.tsx +5 -0
- package/src/components/companion/VrmStage.tsx +2 -0
- package/src/components/companion/__tests__/walletUtils.test.ts +742 -0
- package/src/components/companion/walletUtils.ts +290 -0
- package/src/components/companion-shell-styles.test.ts +146 -0
- package/src/components/companion-shell-styles.ts +270 -0
- package/src/components/confirm-delete-control.tsx +69 -0
- package/src/components/conversations/ConversationListItem.tsx +185 -0
- package/src/components/conversations/conversation-utils.ts +151 -0
- package/src/components/format.ts +131 -0
- package/src/components/index.ts +92 -0
- package/src/components/inventory/CopyableAddress.tsx +41 -0
- package/src/components/inventory/InventoryToolbar.tsx +142 -0
- package/src/components/inventory/NftGrid.tsx +99 -0
- package/src/components/inventory/TokenLogo.tsx +71 -0
- package/src/components/inventory/TokensTable.tsx +216 -0
- package/src/components/inventory/constants.ts +170 -0
- package/src/components/inventory/index.ts +29 -0
- package/src/components/inventory/media-url.test.ts +38 -0
- package/src/components/inventory/media-url.ts +36 -0
- package/src/components/inventory/useInventoryData.ts +460 -0
- package/src/components/knowledge-upload-image.ts +215 -0
- package/src/components/labels.ts +46 -0
- package/src/components/onboarding/ActivateStep.tsx +30 -0
- package/src/components/onboarding/ConnectionStep.tsx +1530 -0
- package/src/components/onboarding/OnboardingPanel.tsx +39 -0
- package/src/components/onboarding/OnboardingStepNav.tsx +31 -0
- package/src/components/onboarding/PermissionsStep.tsx +20 -0
- package/src/components/onboarding/RpcStep.tsx +402 -0
- package/src/components/onboarding/WakeUpStep.tsx +184 -0
- package/src/components/permissions/PermissionIcon.tsx +25 -0
- package/src/components/permissions/StreamingPermissions.tsx +376 -0
- package/src/components/plugins/showcase-data.ts +481 -0
- package/src/components/shared/ShellHeaderControls.tsx +193 -0
- package/src/components/skeletons.tsx +88 -0
- package/src/components/stream/ActivityFeed.tsx +113 -0
- package/src/components/stream/AvatarPip.tsx +10 -0
- package/src/components/stream/ChatContent.tsx +126 -0
- package/src/components/stream/ChatTicker.tsx +55 -0
- package/src/components/stream/IdleContent.tsx +73 -0
- package/src/components/stream/StatusBar.tsx +469 -0
- package/src/components/stream/StreamSettings.tsx +506 -0
- package/src/components/stream/StreamTerminal.tsx +94 -0
- package/src/components/stream/StreamVoiceConfig.tsx +160 -0
- package/src/components/stream/helpers.ts +134 -0
- package/src/components/stream/overlays/OverlayLayer.tsx +75 -0
- package/src/components/stream/overlays/built-in/ActionTickerWidget.tsx +64 -0
- package/src/components/stream/overlays/built-in/AlertPopupWidget.tsx +87 -0
- package/src/components/stream/overlays/built-in/BrandingWidget.tsx +51 -0
- package/src/components/stream/overlays/built-in/CustomHtmlWidget.tsx +105 -0
- package/src/components/stream/overlays/built-in/PeonGlassWidget.tsx +265 -0
- package/src/components/stream/overlays/built-in/PeonHudWidget.tsx +247 -0
- package/src/components/stream/overlays/built-in/PeonSakuraWidget.tsx +278 -0
- package/src/components/stream/overlays/built-in/ThoughtBubbleWidget.tsx +77 -0
- package/src/components/stream/overlays/built-in/ViewerCountWidget.tsx +46 -0
- package/src/components/stream/overlays/built-in/index.ts +13 -0
- package/src/components/stream/overlays/registry.ts +22 -0
- package/src/components/stream/overlays/types.ts +90 -0
- package/src/components/stream/overlays/useOverlayLayout.ts +218 -0
- package/src/components/trajectory-format.ts +50 -0
- package/src/components/ui-badges.tsx +109 -0
- package/src/components/ui-switch.tsx +57 -0
- package/src/components/vector-browser-three.ts +27 -0
- package/src/config/config-catalog.ts +1092 -0
- package/src/config/config-field.tsx +1900 -0
- package/src/config/config-renderer.tsx +730 -0
- package/src/config/index.ts +11 -0
- package/src/config/ui-renderer.tsx +1751 -0
- package/src/config/ui-spec.ts +256 -0
- package/src/events/index.ts +89 -0
- package/src/hooks/index.ts +13 -0
- package/src/hooks/useBugReport.tsx +43 -0
- package/src/hooks/useCanvasWindow.ts +372 -0
- package/src/hooks/useChatAvatarVoice.ts +111 -0
- package/src/hooks/useContextMenu.ts +127 -0
- package/src/hooks/useKeyboardShortcuts.ts +86 -0
- package/src/hooks/useLifoSync.ts +143 -0
- package/src/hooks/useMemoryMonitor.ts +335 -0
- package/src/hooks/useRenderGuard.ts +43 -0
- package/src/hooks/useRetakeCapture.ts +67 -0
- package/src/hooks/useStreamPopoutNavigation.ts +27 -0
- package/src/hooks/useTimeout.ts +37 -0
- package/src/hooks/useVoiceChat.ts +1443 -0
- package/src/hooks/useWhatsAppPairing.ts +123 -0
- package/src/i18n/index.ts +76 -0
- package/src/i18n/locales/en.json +1196 -0
- package/src/i18n/locales/es.json +1196 -0
- package/src/i18n/locales/ko.json +1196 -0
- package/src/i18n/locales/pt.json +1196 -0
- package/src/i18n/locales/zh-CN.json +1196 -0
- package/src/i18n/messages.ts +21 -0
- package/src/index.ts +6 -0
- package/src/navigation/index.ts +282 -0
- package/src/navigation.test.ts +189 -0
- package/src/platform/browser-launch.test.ts +94 -0
- package/src/platform/browser-launch.ts +149 -0
- package/src/platform/index.ts +261 -0
- package/src/platform/init.ts +236 -0
- package/src/providers/index.ts +82 -0
- package/src/state/AppContext.tsx +5737 -0
- package/src/state/index.ts +6 -0
- package/src/state/internal.ts +77 -0
- package/src/state/parsers.test.ts +124 -0
- package/src/state/parsers.ts +309 -0
- package/src/state/persistence.ts +278 -0
- package/src/state/types.ts +694 -0
- package/src/state/ui-preferences.ts +3 -0
- package/src/state/useApp.ts +23 -0
- package/src/state/vrm.ts +76 -0
- package/src/stories/AppMockProvider.tsx +32 -0
- package/src/stories/ChatEmptyState.stories.tsx +27 -0
- package/src/stories/ChatMessage.stories.tsx +115 -0
- package/src/stories/CompanionHeader.stories.tsx +74 -0
- package/src/stories/CompanionView.stories.tsx +33 -0
- package/src/stories/ConversationListItem.stories.tsx +102 -0
- package/src/stories/TypingIndicator.stories.tsx +28 -0
- package/src/styles/anime.css +6324 -0
- package/src/styles/base.css +196 -0
- package/src/styles/onboarding-game.css +716 -0
- package/src/styles/styles.css +2085 -0
- package/src/styles/xterm.css +241 -0
- package/src/types/index.ts +715 -0
- package/src/types/react-test-renderer.d.ts +45 -0
- package/src/utils/asset-url.ts +110 -0
- package/src/utils/assistant-text.ts +172 -0
- package/src/utils/clipboard.ts +41 -0
- package/src/utils/desktop-dialogs.ts +80 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/number-parsing.ts +125 -0
- package/src/utils/openExternalUrl.ts +20 -0
- package/src/utils/spoken-text.ts +65 -0
- package/src/utils/streaming-text.ts +120 -0
- package/src/voice/index.ts +1 -0
- package/src/voice/types.ts +197 -0
- package/test/app/AppContext.pty-sessions.test.tsx +143 -0
- package/test/app/MessageContent.test.tsx +326 -0
- package/test/app/PermissionsOnboarding.test.tsx +216 -0
- package/test/app/PermissionsSection.test.tsx +186 -0
- package/test/app/advanced-trajectory-fine-tuning.e2e.test.ts +397 -0
- package/test/app/agent-activity-box.test.tsx +132 -0
- package/test/app/agent-transfer-lock.test.ts +274 -0
- package/test/app/api-client-electron-fallback.test.ts +139 -0
- package/test/app/api-client-timeout.test.ts +75 -0
- package/test/app/api-client-ws.test.ts +98 -0
- package/test/app/api-client.ws-max-reconnect.test.ts +139 -0
- package/test/app/api-client.ws-reconnect.test.ts +157 -0
- package/test/app/app-context-autonomy-events.test.ts +478 -0
- package/test/app/apps-page-view.test.ts +114 -0
- package/test/app/apps-view.test.ts +769 -0
- package/test/app/autonomous-workflows.e2e.test.ts +765 -0
- package/test/app/autonomy-events.test.ts +150 -0
- package/test/app/avatar-selector.test.tsx +52 -0
- package/test/app/bsc-trade-panel.test.tsx +134 -0
- package/test/app/bug-report-modal.test.tsx +353 -0
- package/test/app/character-customization.e2e.test.ts +1168 -0
- package/test/app/chat-advanced-features.e2e.test.ts +706 -0
- package/test/app/chat-composer.test.tsx +181 -0
- package/test/app/chat-language-header.test.ts +64 -0
- package/test/app/chat-message.test.tsx +222 -0
- package/test/app/chat-modal-view.test.tsx +191 -0
- package/test/app/chat-routine-filter.test.ts +96 -0
- package/test/app/chat-send-lock.test.ts +1302 -0
- package/test/app/chat-stream-api-client.test.tsx +390 -0
- package/test/app/chat-view-game-modal.test.tsx +661 -0
- package/test/app/chat-view.test.tsx +877 -0
- package/test/app/cloud-api.e2e.test.ts +258 -0
- package/test/app/cloud-login-flow.e2e.test.ts +494 -0
- package/test/app/cloud-login-lock.test.ts +411 -0
- package/test/app/command-palette.test.tsx +184 -0
- package/test/app/command-registry.test.ts +75 -0
- package/test/app/companion-greeting-wave.test.tsx +443 -0
- package/test/app/companion-stale-conversation.test.tsx +424 -0
- package/test/app/companion-view.test.tsx +686 -0
- package/test/app/confirm-delete-control.test.ts +79 -0
- package/test/app/confirm-modal.test.tsx +219 -0
- package/test/app/connectors-ui.e2e.test.ts +508 -0
- package/test/app/conversations-sidebar-game-modal.test.tsx +260 -0
- package/test/app/conversations-sidebar.test.tsx +160 -0
- package/test/app/custom-actions-smoke.test.ts +387 -0
- package/test/app/custom-avatar-api-client.test.ts +207 -0
- package/test/app/desktop-utils.test.ts +145 -0
- package/test/app/electrobun-rpc-bridge.test.ts +83 -0
- package/test/app/events.test.ts +88 -0
- package/test/app/export-import-flows.e2e.test.ts +700 -0
- package/test/app/fine-tuning-view.test.ts +471 -0
- package/test/app/game-view-auth-session.test.tsx +186 -0
- package/test/app/game-view.test.ts +444 -0
- package/test/app/global-emote-overlay.test.tsx +106 -0
- package/test/app/header-status.test.tsx +149 -0
- package/test/app/i18n.test.ts +152 -0
- package/test/app/inventory-bsc-view.test.ts +908 -0
- package/test/app/knowledge-ui.e2e.test.ts +762 -0
- package/test/app/knowledge-upload-helpers.test.ts +124 -0
- package/test/app/lifecycle-lock.test.ts +267 -0
- package/test/app/lifo-popout-utils.test.ts +208 -0
- package/test/app/lifo-safe-endpoint.test.ts +34 -0
- package/test/app/loading-screen.test.tsx +45 -0
- package/test/app/memory-monitor.test.ts +332 -0
- package/test/app/navigation.test.tsx +29 -0
- package/test/app/onboarding-finish-lock.test.ts +500 -0
- package/test/app/onboarding-language.test.tsx +161 -0
- package/test/app/onboarding-steps.test.tsx +371 -0
- package/test/app/open-external-url.test.ts +65 -0
- package/test/app/pages-navigation-smoke.e2e.test.ts +633 -0
- package/test/app/pairing-lock.test.ts +260 -0
- package/test/app/pairing-view.test.tsx +74 -0
- package/test/app/permissions-section.test.ts +432 -0
- package/test/app/plugin-bridge.test.ts +109 -0
- package/test/app/plugins-ui.e2e.test.ts +605 -0
- package/test/app/plugins-view-game-modal.test.tsx +650 -0
- package/test/app/plugins-view-toggle-restart.test.ts +129 -0
- package/test/app/provider-dropdown-default.test.tsx +164 -0
- package/test/app/restart-banner.test.tsx +197 -0
- package/test/app/retake-capture.test.ts +84 -0
- package/test/app/sandbox-api-client.test.ts +108 -0
- package/test/app/save-command-modal.test.tsx +109 -0
- package/test/app/secrets-view.test.tsx +92 -0
- package/test/app/settings-control-styles.test.tsx +142 -0
- package/test/app/settings-reset.e2e.test.ts +728 -0
- package/test/app/settings-sections.e2e.test.ts +614 -0
- package/test/app/shared-format.test.ts +44 -0
- package/test/app/shared-switch.test.ts +69 -0
- package/test/app/shell-mode-switching.e2e.test.ts +829 -0
- package/test/app/shell-mode-tab-memory.test.tsx +283 -0
- package/test/app/shell-overlays.test.tsx +50 -0
- package/test/app/shortcuts-overlay.test.tsx +111 -0
- package/test/app/sse-interruption.test.ts +122 -0
- package/test/app/startup-asset-missing.e2e.test.ts +126 -0
- package/test/app/startup-backend-missing.e2e.test.ts +118 -0
- package/test/app/startup-chat.e2e.test.ts +305 -0
- package/test/app/startup-conversation-restore.test.tsx +344 -0
- package/test/app/startup-failure-view.test.tsx +103 -0
- package/test/app/startup-onboarding.e2e.test.ts +612 -0
- package/test/app/startup-timeout.test.tsx +80 -0
- package/test/app/startup-token-401.e2e.test.ts +103 -0
- package/test/app/stream-helpers.test.ts +46 -0
- package/test/app/stream-popout-navigation.test.tsx +41 -0
- package/test/app/stream-status-bar.test.tsx +89 -0
- package/test/app/theme-toggle.test.tsx +33 -0
- package/test/app/training-api-client.test.ts +128 -0
- package/test/app/trajectories-view.test.tsx +220 -0
- package/test/app/triggers-api-client.test.ts +77 -0
- package/test/app/triggers-navigation.test.ts +118 -0
- package/test/app/triggers-view.e2e.test.ts +674 -0
- package/test/app/update-channel-lock.test.ts +259 -0
- package/test/app/vector-browser.async-cleanup.test.tsx +367 -0
- package/test/app/vector-browser.e2e.test.ts +653 -0
- package/test/app/vrm-stage.test.tsx +351 -0
- package/test/app/vrm-viewer.test.tsx +298 -0
- package/test/app/wallet-api-save-lock.test.ts +277 -0
- package/test/app/wallet-hooks.test.ts +405 -0
- package/test/app/wallet-ui-flows.e2e.test.ts +556 -0
- package/test/avatar/asset-url.test.ts +90 -0
- package/test/avatar/avatar-selector.test.ts +173 -0
- package/test/avatar/mixamo-vrm-rig-map.test.ts +111 -0
- package/test/avatar/voice-chat-streaming-text.test.ts +96 -0
- package/test/avatar/voice-chat.test.ts +391 -0
- package/test/ui/command-palette-commands.test.ts +57 -0
- package/test/ui/ui-renderer.test.ts +39 -0
- package/tsconfig.build.json +19 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,4496 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Global application state via React Context.
|
|
4
|
+
*
|
|
5
|
+
* Children access state and actions through the useApp() hook.
|
|
6
|
+
*/
|
|
7
|
+
import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
|
8
|
+
import { prepareDraftForSave } from "../actions/character";
|
|
9
|
+
import { client, MiladyClient, } from "../api";
|
|
10
|
+
import { buildAutonomyGapReplayRequests, hasPendingAutonomyGaps, markPendingAutonomyGapsPartial, mergeAutonomyEvents, } from "../autonomy";
|
|
11
|
+
import { getBackendStartupTimeoutMs, invokeDesktopBridgeRequest, } from "../bridge";
|
|
12
|
+
import { expandSavedCustomCommand, isRoutineCodingAgentMessage, loadSavedCustomCommands, normalizeSlashCommandName, } from "../chat";
|
|
13
|
+
import { mapServerTasksToSessions } from "../coding";
|
|
14
|
+
import { dispatchAppEmoteEvent } from "../events";
|
|
15
|
+
import { createTranslator, normalizeLanguage, t as translateText, } from "../i18n";
|
|
16
|
+
import { pathForTab, tabFromPath } from "../navigation";
|
|
17
|
+
import { getMissingOnboardingPermissions } from "../platform";
|
|
18
|
+
import { alertDesktopMessage, confirmDesktopAction, copyTextToClipboard, openExternalUrl, resolveApiUrl, } from "../utils";
|
|
19
|
+
import { AGENT_READY_TIMEOUT_MS, AGENT_TRANSFER_MIN_PASSWORD_LENGTH, AppContext, applyUiTheme, asApiLikeError, formatSearchBullet, formatStartupErrorDetail, LIFECYCLE_MESSAGES, loadActiveConversationId, loadAvatarIndex, loadChatAvatarVisible, loadChatMode, loadChatVoiceMuted, loadCompanionMessageCutoffTs, loadLastNativeTab, loadUiLanguage, loadUiShellMode, loadUiTheme, mergeStreamingText, normalizeAvatarIndex, normalizeCustomActionName, normalizeUiShellMode, normalizeUiTheme, ONBOARDING_PERMISSION_LABELS, parseAgentStatusEvent, parseCustomActionParams, parseProactiveMessageEvent, parseSlashCommandInput, parseStreamEventEnvelopeEvent, saveActiveConversationId, saveAvatarIndex, saveChatAvatarVisible, saveChatMode, saveChatVoiceMuted, saveCompanionMessageCutoffTs, saveLastNativeTab, saveUiLanguage, saveUiShellMode, saveUiTheme, shouldApplyFinalStreamText, } from "./internal";
|
|
20
|
+
export { AGENT_READY_TIMEOUT_MS, AGENT_STATES, AGENT_TRANSFER_MIN_PASSWORD_LENGTH, AppContext, applyUiTheme, asApiLikeError, computeStreamingDelta, formatSearchBullet, formatStartupErrorDetail, getCompanionBackgroundUrl, getVrmBackgroundUrl, getVrmPreviewUrl, getVrmTitle, getVrmUrl, isOfficialVrmIndex, LIFECYCLE_MESSAGES, loadAvatarIndex, loadChatAvatarVisible, loadChatMode, loadChatVoiceMuted, loadUiLanguage, loadUiShellMode, loadUiTheme, mergeStreamingText, normalizeAvatarIndex, normalizeCustomActionName, normalizeStreamComparisonText, normalizeUiShellMode, normalizeUiTheme, ONBOARDING_PERMISSION_LABELS, parseAgentStartupDiagnostics, parseAgentStatusEvent, parseConversationMessageEvent, parseCustomActionParams, parseProactiveMessageEvent, parseSlashCommandInput, parseStreamEventEnvelopeEvent, saveAvatarIndex, saveChatAvatarVisible, saveChatMode, saveChatVoiceMuted, saveUiLanguage, saveUiShellMode, saveUiTheme, shouldApplyFinalStreamText, useApp, VRM_COUNT, } from "./internal";
|
|
21
|
+
import { ConfirmModal, PromptModal, useConfirm, usePrompt, } from "../components/ConfirmModal";
|
|
22
|
+
const GREETING_EMOTE_DELAY_MS = 1400;
|
|
23
|
+
const GREETING_WAVE_EMOTE = {
|
|
24
|
+
emoteId: "wave",
|
|
25
|
+
path: "/animations/emotes/waving-both-hands.glb",
|
|
26
|
+
duration: 2.5,
|
|
27
|
+
loop: false,
|
|
28
|
+
showOverlay: false,
|
|
29
|
+
};
|
|
30
|
+
const ELIZA_CLOUD_LOGIN_POLL_INTERVAL_MS = 1000;
|
|
31
|
+
const ELIZA_CLOUD_LOGIN_TIMEOUT_MS = 300_000;
|
|
32
|
+
const ELIZA_CLOUD_LOGIN_MAX_CONSECUTIVE_ERRORS = 3;
|
|
33
|
+
function normalizeAppEmoteEvent(data) {
|
|
34
|
+
const emoteId = typeof data.emoteId === "string" ? data.emoteId : null;
|
|
35
|
+
const path = typeof data.path === "string"
|
|
36
|
+
? data.path
|
|
37
|
+
: typeof data.glbPath === "string"
|
|
38
|
+
? data.glbPath
|
|
39
|
+
: null;
|
|
40
|
+
if (!emoteId || !path)
|
|
41
|
+
return null;
|
|
42
|
+
return {
|
|
43
|
+
emoteId,
|
|
44
|
+
path,
|
|
45
|
+
duration: typeof data.duration === "number" && Number.isFinite(data.duration)
|
|
46
|
+
? data.duration
|
|
47
|
+
: 3,
|
|
48
|
+
loop: data.loop === true,
|
|
49
|
+
showOverlay: data.showOverlay !== false,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function shouldKeepConversationMessage(message) {
|
|
53
|
+
if (message.role !== "assistant")
|
|
54
|
+
return true;
|
|
55
|
+
if (message.text.trim().length > 0)
|
|
56
|
+
return true;
|
|
57
|
+
return Boolean(message.blocks?.length);
|
|
58
|
+
}
|
|
59
|
+
function filterRenderableConversationMessages(messages) {
|
|
60
|
+
return messages.filter((message) => shouldKeepConversationMessage(message));
|
|
61
|
+
}
|
|
62
|
+
const COMPANION_STALE_THREAD_MAX_AGE_MS = 30 * 60 * 1000;
|
|
63
|
+
const COMPANION_STALE_THREAD_VISIBLE_MESSAGE_LIMIT = 2;
|
|
64
|
+
function isPersistedGreetingMessage(message) {
|
|
65
|
+
return (message.role === "assistant" &&
|
|
66
|
+
message.source === "agent_greeting" &&
|
|
67
|
+
message.text.trim().length > 0);
|
|
68
|
+
}
|
|
69
|
+
function shouldStartFreshCompanionConversation(messages, now = Date.now()) {
|
|
70
|
+
const visibleMessages = messages
|
|
71
|
+
.filter((message) => shouldKeepConversationMessage(message))
|
|
72
|
+
.filter((message) => !isRoutineCodingAgentMessage(message))
|
|
73
|
+
.slice(-COMPANION_STALE_THREAD_VISIBLE_MESSAGE_LIMIT);
|
|
74
|
+
if (visibleMessages.length === 0) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (visibleMessages.length === 1 &&
|
|
78
|
+
isPersistedGreetingMessage(visibleMessages[0])) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return visibleMessages.every((message) => {
|
|
82
|
+
if (!Number.isFinite(message.timestamp)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return now - message.timestamp > COMPANION_STALE_THREAD_MAX_AGE_MS;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function isPrivateNetworkHost(host) {
|
|
89
|
+
if (host === "localhost" ||
|
|
90
|
+
host === "127.0.0.1" ||
|
|
91
|
+
host === "::1" ||
|
|
92
|
+
host.endsWith(".local") ||
|
|
93
|
+
host.endsWith(".internal")) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (/^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host))
|
|
97
|
+
return true;
|
|
98
|
+
if (/^192\.168\.\d{1,3}\.\d{1,3}$/.test(host))
|
|
99
|
+
return true;
|
|
100
|
+
if (/^172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3}$/.test(host))
|
|
101
|
+
return true;
|
|
102
|
+
if (/^100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.\d{1,3}\.\d{1,3}$/.test(host)) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
function normalizeRemoteApiBaseInput(rawValue) {
|
|
108
|
+
const trimmed = rawValue.trim();
|
|
109
|
+
if (!trimmed) {
|
|
110
|
+
throw new Error("Enter a backend address.");
|
|
111
|
+
}
|
|
112
|
+
const hasScheme = /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(trimmed);
|
|
113
|
+
const hostGuess = trimmed.replace(/^[a-zA-Z][a-zA-Z\d+.-]*:\/\//, "");
|
|
114
|
+
const guessedHost = hostGuess.split("/")[0]?.replace(/:\d+$/, "") ?? "";
|
|
115
|
+
const defaultProtocol = isPrivateNetworkHost(guessedHost) ? "http" : "https";
|
|
116
|
+
const candidate = hasScheme ? trimmed : `${defaultProtocol}://${trimmed}`;
|
|
117
|
+
let parsed;
|
|
118
|
+
try {
|
|
119
|
+
parsed = new URL(candidate);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
throw new Error("Enter a valid backend address.");
|
|
123
|
+
}
|
|
124
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
125
|
+
throw new Error("Remote backends must use http:// or https://.");
|
|
126
|
+
}
|
|
127
|
+
parsed.pathname = parsed.pathname.replace(/\/+$/, "");
|
|
128
|
+
parsed.search = "";
|
|
129
|
+
parsed.hash = "";
|
|
130
|
+
return parsed.toString().replace(/\/+$/, "");
|
|
131
|
+
}
|
|
132
|
+
function loadSessionApiBase() {
|
|
133
|
+
if (typeof window === "undefined")
|
|
134
|
+
return "";
|
|
135
|
+
return window.sessionStorage.getItem("milady_api_base")?.trim() ?? "";
|
|
136
|
+
}
|
|
137
|
+
function isRemoteApiBase(baseUrl) {
|
|
138
|
+
if (!baseUrl || typeof window === "undefined")
|
|
139
|
+
return false;
|
|
140
|
+
try {
|
|
141
|
+
const parsed = new URL(baseUrl);
|
|
142
|
+
return (parsed.hostname !== window.location.hostname &&
|
|
143
|
+
parsed.hostname !== "localhost" &&
|
|
144
|
+
parsed.hostname !== "127.0.0.1" &&
|
|
145
|
+
parsed.hostname !== "::1");
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// ── Provider ───────────────────────────────────────────────────────────
|
|
152
|
+
export function AppProvider({ children }) {
|
|
153
|
+
const [lastNativeTab, setLastNativeTabState] = useState(loadLastNativeTab);
|
|
154
|
+
// --- Core state ---
|
|
155
|
+
const [tab, setTabRaw] = useState("chat");
|
|
156
|
+
const [uiShellMode, setUiShellModeState] = useState(loadUiShellMode);
|
|
157
|
+
const [uiLanguage, setUiLanguageState] = useState(loadUiLanguage);
|
|
158
|
+
const [uiTheme, setUiThemeState] = useState(loadUiTheme);
|
|
159
|
+
const [connected, setConnected] = useState(false);
|
|
160
|
+
const [agentStatus, setAgentStatus] = useState(null);
|
|
161
|
+
const [onboardingComplete, setOnboardingComplete] = useState(false);
|
|
162
|
+
const [onboardingLoading, setOnboardingLoading] = useState(true);
|
|
163
|
+
const [startupPhase, setStartupPhase] = useState("starting-backend");
|
|
164
|
+
const [startupError, setStartupError] = useState(null);
|
|
165
|
+
const [startupRetryNonce, setStartupRetryNonce] = useState(0);
|
|
166
|
+
const [authRequired, setAuthRequired] = useState(false);
|
|
167
|
+
const [actionNotice, setActionNoticeState] = useState(null);
|
|
168
|
+
const [lifecycleBusy, setLifecycleBusy] = useState(false);
|
|
169
|
+
const [lifecycleAction, setLifecycleAction] = useState(null);
|
|
170
|
+
// --- Deferred restart ---
|
|
171
|
+
const [pendingRestart, setPendingRestart] = useState(false);
|
|
172
|
+
const [pendingRestartReasons, setPendingRestartReasons] = useState([]);
|
|
173
|
+
const [restartBannerDismissed, setRestartBannerDismissed] = useState(false);
|
|
174
|
+
// --- Backend connection state (for crash handling) ---
|
|
175
|
+
const [backendConnection, setBackendConnection] = useState({
|
|
176
|
+
state: "disconnected",
|
|
177
|
+
reconnectAttempt: 0,
|
|
178
|
+
maxReconnectAttempts: 15,
|
|
179
|
+
showDisconnectedUI: false,
|
|
180
|
+
});
|
|
181
|
+
const [backendDisconnectedBannerDismissed, setBackendDisconnectedBannerDismissed,] = useState(false);
|
|
182
|
+
const [systemWarnings, setSystemWarnings] = useState([]);
|
|
183
|
+
// --- Pairing ---
|
|
184
|
+
const [pairingEnabled, setPairingEnabled] = useState(false);
|
|
185
|
+
const [pairingExpiresAt, setPairingExpiresAt] = useState(null);
|
|
186
|
+
const [pairingCodeInput, setPairingCodeInput] = useState("");
|
|
187
|
+
const [pairingError, setPairingError] = useState(null);
|
|
188
|
+
const [pairingBusy, setPairingBusy] = useState(false);
|
|
189
|
+
// --- Chat ---
|
|
190
|
+
const [chatInput, setChatInput] = useState("");
|
|
191
|
+
const [chatSending, setChatSending] = useState(false);
|
|
192
|
+
const [chatFirstTokenReceived, setChatFirstTokenReceived] = useState(false);
|
|
193
|
+
const [chatLastUsage, setChatLastUsage] = useState(null);
|
|
194
|
+
const [chatAvatarVisible, setChatAvatarVisible] = useState(loadChatAvatarVisible);
|
|
195
|
+
const [chatAgentVoiceMuted, setChatAgentVoiceMuted] = useState(loadChatVoiceMuted);
|
|
196
|
+
const [chatMode, setChatMode] = useState(loadChatMode);
|
|
197
|
+
const [chatAvatarSpeaking, setChatAvatarSpeaking] = useState(false);
|
|
198
|
+
const [conversations, setConversations] = useState([]);
|
|
199
|
+
const [activeConversationId, setActiveConversationId] = useState(null);
|
|
200
|
+
const [companionMessageCutoffTs, setCompanionMessageCutoffTs] = useState(loadCompanionMessageCutoffTs);
|
|
201
|
+
const [conversationMessages, setConversationMessages] = useState([]);
|
|
202
|
+
const [autonomousEvents, setAutonomousEvents] = useState([]);
|
|
203
|
+
const [autonomousLatestEventId, setAutonomousLatestEventId] = useState(null);
|
|
204
|
+
const [autonomousRunHealthByRunId, setAutonomousRunHealthByRunId] = useState({});
|
|
205
|
+
const [ptySessions, setPtySessions] = useState([]);
|
|
206
|
+
const [unreadConversations, setUnreadConversations] = useState(new Set());
|
|
207
|
+
const autonomousStoreRef = useRef({
|
|
208
|
+
eventsById: {},
|
|
209
|
+
eventOrder: [],
|
|
210
|
+
runIndex: {},
|
|
211
|
+
watermark: null,
|
|
212
|
+
});
|
|
213
|
+
const autonomousEventsRef = useRef([]);
|
|
214
|
+
const autonomousLatestEventIdRef = useRef(null);
|
|
215
|
+
const autonomousRunHealthByRunIdRef = useRef({});
|
|
216
|
+
const autonomousReplayInFlightRef = useRef(false);
|
|
217
|
+
const activeConversationIdRef = useRef(null);
|
|
218
|
+
const conversationMessagesRef = useRef([]);
|
|
219
|
+
const conversationsRef = useRef([]);
|
|
220
|
+
const conversationHydrationEpochRef = useRef(0);
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
autonomousEventsRef.current = autonomousEvents;
|
|
223
|
+
}, [autonomousEvents]);
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
autonomousLatestEventIdRef.current = autonomousLatestEventId;
|
|
226
|
+
}, [autonomousLatestEventId]);
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
autonomousRunHealthByRunIdRef.current = autonomousRunHealthByRunId;
|
|
229
|
+
}, [autonomousRunHealthByRunId]);
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
conversationMessagesRef.current = conversationMessages;
|
|
232
|
+
}, [conversationMessages]);
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
conversationsRef.current = conversations;
|
|
235
|
+
}, [conversations]);
|
|
236
|
+
useEffect(() => {
|
|
237
|
+
saveChatAvatarVisible(chatAvatarVisible);
|
|
238
|
+
}, [chatAvatarVisible]);
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
saveChatVoiceMuted(chatAgentVoiceMuted);
|
|
241
|
+
}, [chatAgentVoiceMuted]);
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
saveChatMode(chatMode);
|
|
244
|
+
}, [chatMode]);
|
|
245
|
+
useEffect(() => {
|
|
246
|
+
saveActiveConversationId(activeConversationId);
|
|
247
|
+
}, [activeConversationId]);
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
saveCompanionMessageCutoffTs(companionMessageCutoffTs);
|
|
250
|
+
}, [companionMessageCutoffTs]);
|
|
251
|
+
// --- Triggers ---
|
|
252
|
+
const [triggers, setTriggers] = useState([]);
|
|
253
|
+
const [triggersLoading, setTriggersLoading] = useState(false);
|
|
254
|
+
const [triggersSaving, setTriggersSaving] = useState(false);
|
|
255
|
+
const [triggerRunsById, setTriggerRunsById] = useState({});
|
|
256
|
+
const [triggerHealth, setTriggerHealth] = useState(null);
|
|
257
|
+
const [triggerError, setTriggerError] = useState(null);
|
|
258
|
+
// --- Plugins ---
|
|
259
|
+
const [plugins, setPlugins] = useState([]);
|
|
260
|
+
const [pluginFilter, setPluginFilter] = useState("all");
|
|
261
|
+
const [pluginStatusFilter, setPluginStatusFilter] = useState("all");
|
|
262
|
+
const [pluginSearch, setPluginSearch] = useState("");
|
|
263
|
+
const [pluginSettingsOpen, setPluginSettingsOpen] = useState(new Set());
|
|
264
|
+
const [pluginAdvancedOpen, setPluginAdvancedOpen] = useState(new Set());
|
|
265
|
+
const [pluginSaving, setPluginSaving] = useState(new Set());
|
|
266
|
+
const [pluginSaveSuccess, setPluginSaveSuccess] = useState(new Set());
|
|
267
|
+
// --- Skills ---
|
|
268
|
+
const [skills, setSkills] = useState([]);
|
|
269
|
+
const [skillsSubTab, setSkillsSubTab] = useState("my");
|
|
270
|
+
const [skillCreateFormOpen, setSkillCreateFormOpen] = useState(false);
|
|
271
|
+
const [skillCreateName, setSkillCreateName] = useState("");
|
|
272
|
+
const [skillCreateDescription, setSkillCreateDescription] = useState("");
|
|
273
|
+
const [skillCreating, setSkillCreating] = useState(false);
|
|
274
|
+
const [skillReviewReport, setSkillReviewReport] = useState(null);
|
|
275
|
+
const [skillReviewId, setSkillReviewId] = useState("");
|
|
276
|
+
const [skillReviewLoading, setSkillReviewLoading] = useState(false);
|
|
277
|
+
const [skillToggleAction, setSkillToggleAction] = useState("");
|
|
278
|
+
const [skillsMarketplaceQuery, setSkillsMarketplaceQuery] = useState("");
|
|
279
|
+
const [skillsMarketplaceResults, setSkillsMarketplaceResults] = useState([]);
|
|
280
|
+
const [skillsMarketplaceError, setSkillsMarketplaceError] = useState("");
|
|
281
|
+
const [skillsMarketplaceLoading, setSkillsMarketplaceLoading] = useState(false);
|
|
282
|
+
const [skillsMarketplaceAction, setSkillsMarketplaceAction] = useState("");
|
|
283
|
+
const [skillsMarketplaceManualGithubUrl, setSkillsMarketplaceManualGithubUrl,] = useState("");
|
|
284
|
+
// --- Logs ---
|
|
285
|
+
const [logs, setLogs] = useState([]);
|
|
286
|
+
const [logSources, setLogSources] = useState([]);
|
|
287
|
+
const [logTags, setLogTags] = useState([]);
|
|
288
|
+
const [logTagFilter, setLogTagFilter] = useState("");
|
|
289
|
+
const [logLevelFilter, setLogLevelFilter] = useState("");
|
|
290
|
+
const [logSourceFilter, setLogSourceFilter] = useState("");
|
|
291
|
+
// --- Wallet / Inventory ---
|
|
292
|
+
const [walletAddresses, setWalletAddresses] = useState(null);
|
|
293
|
+
const [walletConfig, setWalletConfig] = useState(null);
|
|
294
|
+
const [walletBalances, setWalletBalances] = useState(null);
|
|
295
|
+
const [walletNfts, setWalletNfts] = useState(null);
|
|
296
|
+
const [walletLoading, setWalletLoading] = useState(false);
|
|
297
|
+
const [walletNftsLoading, setWalletNftsLoading] = useState(false);
|
|
298
|
+
const [inventoryView, setInventoryView] = useState("tokens");
|
|
299
|
+
const [walletExportData, setWalletExportData] = useState(null);
|
|
300
|
+
const [walletExportVisible, setWalletExportVisible] = useState(false);
|
|
301
|
+
const [walletApiKeySaving, setWalletApiKeySaving] = useState(false);
|
|
302
|
+
const [inventorySort, setInventorySort] = useState("value");
|
|
303
|
+
const [inventoryChainFocus, setInventoryChainFocus] = useState("all");
|
|
304
|
+
const [walletError, setWalletError] = useState(null);
|
|
305
|
+
// --- ERC-8004 Registry ---
|
|
306
|
+
const [registryStatus, setRegistryStatus] = useState(null);
|
|
307
|
+
const [registryLoading, setRegistryLoading] = useState(false);
|
|
308
|
+
const [registryRegistering, setRegistryRegistering] = useState(false);
|
|
309
|
+
const [registryError, setRegistryError] = useState(null);
|
|
310
|
+
// --- Drop / Mint ---
|
|
311
|
+
const [dropStatus, setDropStatus] = useState(null);
|
|
312
|
+
const [dropLoading, setDropLoading] = useState(false);
|
|
313
|
+
const [mintInProgress, setMintInProgress] = useState(false);
|
|
314
|
+
const [mintResult, setMintResult] = useState(null);
|
|
315
|
+
const [mintError, setMintError] = useState(null);
|
|
316
|
+
const [mintShiny, setMintShiny] = useState(false);
|
|
317
|
+
// --- Whitelist ---
|
|
318
|
+
const [whitelistStatus, setWhitelistStatus] = useState(null);
|
|
319
|
+
const [whitelistLoading, setWhitelistLoading] = useState(false);
|
|
320
|
+
const [twitterVerifyMessage] = useState(null);
|
|
321
|
+
const [twitterVerifyUrl] = useState("");
|
|
322
|
+
const [twitterVerifying] = useState(false);
|
|
323
|
+
// --- Character ---
|
|
324
|
+
const [characterData, setCharacterData] = useState(null);
|
|
325
|
+
const [characterLoading, setCharacterLoading] = useState(false);
|
|
326
|
+
const [characterSaving, setCharacterSaving] = useState(false);
|
|
327
|
+
const [characterSaveSuccess, setCharacterSaveSuccess] = useState(null);
|
|
328
|
+
const [characterSaveError, setCharacterSaveError] = useState(null);
|
|
329
|
+
const [characterDraft, setCharacterDraft] = useState({});
|
|
330
|
+
const [selectedVrmIndex, setSelectedVrmIndexRaw] = useState(loadAvatarIndex);
|
|
331
|
+
const [customVrmUrl, setCustomVrmUrl] = useState("");
|
|
332
|
+
const [customBackgroundUrl, setCustomBackgroundUrl] = useState("");
|
|
333
|
+
// Wrap setter to also persist to localStorage
|
|
334
|
+
const setSelectedVrmIndex = useCallback((v) => {
|
|
335
|
+
const normalized = normalizeAvatarIndex(v);
|
|
336
|
+
setSelectedVrmIndexRaw(normalized);
|
|
337
|
+
saveAvatarIndex(normalized);
|
|
338
|
+
// Sync to server so headless stream capture uses the same avatar
|
|
339
|
+
client.saveStreamSettings({ avatarIndex: normalized }).catch(() => { });
|
|
340
|
+
}, []);
|
|
341
|
+
// --- Eliza Cloud ---
|
|
342
|
+
const [elizaCloudEnabled, setElizaCloudEnabled] = useState(false);
|
|
343
|
+
const [elizaCloudConnected, setElizaCloudConnected] = useState(false);
|
|
344
|
+
const [elizaCloudCredits, setElizaCloudCredits] = useState(null);
|
|
345
|
+
const [elizaCloudCreditsLow, setElizaCloudCreditsLow] = useState(false);
|
|
346
|
+
const [elizaCloudCreditsCritical, setElizaCloudCreditsCritical] = useState(false);
|
|
347
|
+
const [elizaCloudTopUpUrl, setElizaCloudTopUpUrl] = useState("/cloud/billing");
|
|
348
|
+
const [elizaCloudUserId, setElizaCloudUserId] = useState(null);
|
|
349
|
+
const [cloudDashboardView, setCloudDashboardView] = useState("billing");
|
|
350
|
+
const [elizaCloudLoginBusy, setElizaCloudLoginBusy] = useState(false);
|
|
351
|
+
const [elizaCloudLoginError, setElizaCloudLoginError] = useState(null);
|
|
352
|
+
const [elizaCloudDisconnecting, setElizaCloudDisconnecting] = useState(false);
|
|
353
|
+
// --- Updates ---
|
|
354
|
+
const [updateStatus, setUpdateStatus] = useState(null);
|
|
355
|
+
const [updateLoading, setUpdateLoading] = useState(false);
|
|
356
|
+
const [updateChannelSaving, setUpdateChannelSaving] = useState(false);
|
|
357
|
+
// --- Extension ---
|
|
358
|
+
const [extensionStatus, setExtensionStatus] = useState(null);
|
|
359
|
+
const [extensionChecking, setExtensionChecking] = useState(false);
|
|
360
|
+
// --- Store ---
|
|
361
|
+
const [storePlugins, setStorePlugins] = useState([]);
|
|
362
|
+
const [storeSearch, setStoreSearch] = useState("");
|
|
363
|
+
const [storeFilter, setStoreFilter] = useState("all");
|
|
364
|
+
const [storeLoading, setStoreLoading] = useState(false);
|
|
365
|
+
const [storeInstalling, setStoreInstalling] = useState(new Set());
|
|
366
|
+
const [storeUninstalling, setStoreUninstalling] = useState(new Set());
|
|
367
|
+
const [storeError, setStoreError] = useState(null);
|
|
368
|
+
const [storeDetailPlugin, setStoreDetailPlugin] = useState(null);
|
|
369
|
+
const [storeSubTab, setStoreSubTab] = useState("plugins");
|
|
370
|
+
// --- Catalog ---
|
|
371
|
+
const [catalogSkills, setCatalogSkills] = useState([]);
|
|
372
|
+
const [catalogTotal, setCatalogTotal] = useState(0);
|
|
373
|
+
const [catalogPage, setCatalogPage] = useState(1);
|
|
374
|
+
const [catalogTotalPages, setCatalogTotalPages] = useState(1);
|
|
375
|
+
const [catalogSort, setCatalogSort] = useState("downloads");
|
|
376
|
+
const [catalogSearch, setCatalogSearch] = useState("");
|
|
377
|
+
const [catalogLoading, setCatalogLoading] = useState(false);
|
|
378
|
+
const [catalogError, setCatalogError] = useState(null);
|
|
379
|
+
const [catalogDetailSkill, setCatalogDetailSkill] = useState(null);
|
|
380
|
+
const [catalogInstalling, setCatalogInstalling] = useState(new Set());
|
|
381
|
+
const [catalogUninstalling, setCatalogUninstalling] = useState(new Set());
|
|
382
|
+
// --- Workbench ---
|
|
383
|
+
const [workbenchLoading, setWorkbenchLoading] = useState(false);
|
|
384
|
+
const [workbench, setWorkbench] = useState(null);
|
|
385
|
+
const [workbenchTasksAvailable, setWorkbenchTasksAvailable] = useState(false);
|
|
386
|
+
const [workbenchTriggersAvailable, setWorkbenchTriggersAvailable] = useState(false);
|
|
387
|
+
const [workbenchTodosAvailable, setWorkbenchTodosAvailable] = useState(false);
|
|
388
|
+
// --- Agent export/import ---
|
|
389
|
+
const [exportBusy, setExportBusy] = useState(false);
|
|
390
|
+
const [exportPassword, setExportPassword] = useState("");
|
|
391
|
+
const [exportIncludeLogs, setExportIncludeLogs] = useState(false);
|
|
392
|
+
const [exportError, setExportError] = useState(null);
|
|
393
|
+
const [exportSuccess, setExportSuccess] = useState(null);
|
|
394
|
+
const [importBusy, setImportBusy] = useState(false);
|
|
395
|
+
const [importPassword, setImportPassword] = useState("");
|
|
396
|
+
const [importFile, setImportFile] = useState(null);
|
|
397
|
+
const [importError, setImportError] = useState(null);
|
|
398
|
+
const [importSuccess, setImportSuccess] = useState(null);
|
|
399
|
+
// --- Onboarding ---
|
|
400
|
+
const [onboardingStep, setOnboardingStep] = useState("wakeUp");
|
|
401
|
+
const [onboardingOptions, setOnboardingOptions] = useState(null);
|
|
402
|
+
const [onboardingName, setOnboardingName] = useState("Eliza");
|
|
403
|
+
const [onboardingOwnerName, setOnboardingOwnerName] = useState("anon");
|
|
404
|
+
const [onboardingStyle, setOnboardingStyle] = useState("");
|
|
405
|
+
const [onboardingRunMode, setOnboardingRunMode] = useState("");
|
|
406
|
+
const [onboardingCloudProvider, setOnboardingCloudProvider] = useState("");
|
|
407
|
+
const [onboardingSmallModel, setOnboardingSmallModel] = useState("moonshotai/kimi-k2-turbo");
|
|
408
|
+
const [onboardingLargeModel, setOnboardingLargeModel] = useState("moonshotai/kimi-k2-0905");
|
|
409
|
+
const [onboardingProvider, setOnboardingProvider] = useState("");
|
|
410
|
+
const [onboardingApiKey, setOnboardingApiKey] = useState("");
|
|
411
|
+
const [onboardingRemoteApiBase, setOnboardingRemoteApiBase] = useState(loadSessionApiBase);
|
|
412
|
+
const [onboardingRemoteToken, setOnboardingRemoteToken] = useState("");
|
|
413
|
+
const [onboardingRemoteConnecting, setOnboardingRemoteConnecting] = useState(false);
|
|
414
|
+
const [onboardingRemoteError, setOnboardingRemoteError] = useState(null);
|
|
415
|
+
const [onboardingRemoteConnected, setOnboardingRemoteConnected] = useState(() => isRemoteApiBase(loadSessionApiBase()));
|
|
416
|
+
const [onboardingOpenRouterModel, setOnboardingOpenRouterModel] = useState("");
|
|
417
|
+
const [onboardingPrimaryModel, setOnboardingPrimaryModel] = useState("");
|
|
418
|
+
const [onboardingTelegramToken, setOnboardingTelegramToken] = useState("");
|
|
419
|
+
const [onboardingDiscordToken, setOnboardingDiscordToken] = useState("");
|
|
420
|
+
const [onboardingWhatsAppSessionPath, setOnboardingWhatsAppSessionPath] = useState("");
|
|
421
|
+
const [onboardingTwilioAccountSid, setOnboardingTwilioAccountSid] = useState("");
|
|
422
|
+
const [onboardingTwilioAuthToken, setOnboardingTwilioAuthToken] = useState("");
|
|
423
|
+
const [onboardingTwilioPhoneNumber, setOnboardingTwilioPhoneNumber] = useState("");
|
|
424
|
+
const [onboardingBlooioApiKey, setOnboardingBlooioApiKey] = useState("");
|
|
425
|
+
const [onboardingBlooioPhoneNumber, setOnboardingBlooioPhoneNumber] = useState("");
|
|
426
|
+
const [onboardingGithubToken, setOnboardingGithubToken] = useState("");
|
|
427
|
+
const [onboardingSubscriptionTab, setOnboardingSubscriptionTab] = useState("token");
|
|
428
|
+
const [onboardingElizaCloudTab, setOnboardingElizaCloudTab] = useState("login");
|
|
429
|
+
const [onboardingSelectedChains, setOnboardingSelectedChains] = useState(new Set(["evm", "solana"]));
|
|
430
|
+
const [onboardingRpcSelections, setOnboardingRpcSelections] = useState({});
|
|
431
|
+
const [onboardingRpcKeys, setOnboardingRpcKeys] = useState({});
|
|
432
|
+
const [onboardingAvatar, setOnboardingAvatar] = useState(1);
|
|
433
|
+
const [onboardingRestarting, setOnboardingRestarting] = useState(false);
|
|
434
|
+
// --- Command palette ---
|
|
435
|
+
const [commandPaletteOpen, _setCommandPaletteOpen] = useState(false);
|
|
436
|
+
const [commandQuery, setCommandQuery] = useState("");
|
|
437
|
+
const [commandActiveIndex, setCommandActiveIndex] = useState(0);
|
|
438
|
+
// --- Emote picker ---
|
|
439
|
+
const [emotePickerOpen, setEmotePickerOpen] = useState(false);
|
|
440
|
+
// --- MCP ---
|
|
441
|
+
const [mcpConfiguredServers, setMcpConfiguredServers] = useState({});
|
|
442
|
+
const [mcpServerStatuses, setMcpServerStatuses] = useState([]);
|
|
443
|
+
const [mcpMarketplaceQuery, setMcpMarketplaceQuery] = useState("");
|
|
444
|
+
const [mcpMarketplaceResults, setMcpMarketplaceResults] = useState([]);
|
|
445
|
+
const [mcpMarketplaceLoading, setMcpMarketplaceLoading] = useState(false);
|
|
446
|
+
const [mcpAction, setMcpAction] = useState("");
|
|
447
|
+
const [mcpAddingServer, setMcpAddingServer] = useState(null);
|
|
448
|
+
const [mcpAddingResult, setMcpAddingResult] = useState(null);
|
|
449
|
+
const [mcpEnvInputs, setMcpEnvInputs] = useState({});
|
|
450
|
+
const [mcpHeaderInputs, setMcpHeaderInputs] = useState({});
|
|
451
|
+
// --- Share ingest ---
|
|
452
|
+
const [droppedFiles, setDroppedFiles] = useState([]);
|
|
453
|
+
const [shareIngestNotice, setShareIngestNotice] = useState("");
|
|
454
|
+
// --- Chat pending images ---
|
|
455
|
+
const [chatPendingImages, setChatPendingImages] = useState([]);
|
|
456
|
+
// --- Game ---
|
|
457
|
+
const [activeGameApp, setActiveGameApp] = useState("");
|
|
458
|
+
const [activeGameDisplayName, setActiveGameDisplayName] = useState("");
|
|
459
|
+
const [activeGameViewerUrl, setActiveGameViewerUrl] = useState("");
|
|
460
|
+
const [activeGameSandbox, setActiveGameSandbox] = useState("allow-scripts allow-same-origin allow-popups");
|
|
461
|
+
const [activeGamePostMessageAuth, setActiveGamePostMessageAuth] = useState(false);
|
|
462
|
+
const [activeGamePostMessagePayload, setActiveGamePostMessagePayload] = useState(null);
|
|
463
|
+
const [gameOverlayEnabled, setGameOverlayEnabled] = useState(false);
|
|
464
|
+
// --- Admin ---
|
|
465
|
+
const [appsSubTab, setAppsSubTab] = useState("browse");
|
|
466
|
+
const [agentSubTab, setAgentSubTab] = useState("character");
|
|
467
|
+
const [pluginsSubTab, setPluginsSubTab] = useState("features");
|
|
468
|
+
const [databaseSubTab, setDatabaseSubTab] = useState("tables");
|
|
469
|
+
// --- Config ---
|
|
470
|
+
const [configRaw, setConfigRaw] = useState({});
|
|
471
|
+
const [configText, setConfigText] = useState("");
|
|
472
|
+
// --- Refs for timers ---
|
|
473
|
+
const actionNoticeTimer = useRef(null);
|
|
474
|
+
const elizaCloudPollInterval = useRef(null);
|
|
475
|
+
const elizaCloudLoginPollTimer = useRef(null);
|
|
476
|
+
const prevAgentStateRef = useRef(null);
|
|
477
|
+
/** Tracks last agent status to skip no-op updates from WS heartbeats. */
|
|
478
|
+
const agentStatusRef = useRef(null);
|
|
479
|
+
/** Only call setAgentStatus when the payload has materially changed. */
|
|
480
|
+
const setAgentStatusIfChanged = useCallback((next) => {
|
|
481
|
+
const prev = agentStatusRef.current;
|
|
482
|
+
if (prev &&
|
|
483
|
+
next &&
|
|
484
|
+
prev.state === next.state &&
|
|
485
|
+
prev.agentName === next.agentName &&
|
|
486
|
+
prev.model === next.model &&
|
|
487
|
+
prev.startedAt === next.startedAt) {
|
|
488
|
+
return; // identical — skip re-render
|
|
489
|
+
}
|
|
490
|
+
agentStatusRef.current = next;
|
|
491
|
+
setAgentStatus(next);
|
|
492
|
+
}, []);
|
|
493
|
+
const lifecycleBusyRef = useRef(false);
|
|
494
|
+
const lifecycleActionRef = useRef(null);
|
|
495
|
+
/** Synchronous lock for onboarding finish to prevent duplicate same-tick submits. */
|
|
496
|
+
const onboardingFinishBusyRef = useRef(false);
|
|
497
|
+
const pairingBusyRef = useRef(false);
|
|
498
|
+
/** Guards against double-greeting when both init and state-transition paths fire. */
|
|
499
|
+
const greetingFiredRef = useRef(false);
|
|
500
|
+
const greetingInFlightConversationRef = useRef(null);
|
|
501
|
+
const greetingEmoteTimerRef = useRef(null);
|
|
502
|
+
const companionStaleConversationRefreshRef = useRef(null);
|
|
503
|
+
const chatAbortRef = useRef(null);
|
|
504
|
+
/** Synchronous lock so same-tick chat submits cannot double-send. */
|
|
505
|
+
const chatSendBusyRef = useRef(false);
|
|
506
|
+
const chatSendNonceRef = useRef(0);
|
|
507
|
+
/** Synchronous lock for export action to prevent duplicate clicks in the same tick. */
|
|
508
|
+
const exportBusyRef = useRef(false);
|
|
509
|
+
/** Synchronous lock for import action to prevent duplicate clicks in the same tick. */
|
|
510
|
+
const importBusyRef = useRef(false);
|
|
511
|
+
/** Synchronous lock for wallet API key save to prevent duplicate clicks in the same tick. */
|
|
512
|
+
const walletApiKeySavingRef = useRef(false);
|
|
513
|
+
/** Synchronous lock for cloud login action to prevent duplicate clicks in the same tick. */
|
|
514
|
+
const elizaCloudLoginBusyRef = useRef(false);
|
|
515
|
+
/** Synchronous lock for update channel changes to prevent duplicate submits. */
|
|
516
|
+
const updateChannelSavingRef = useRef(false);
|
|
517
|
+
/** Synchronous lock for onboarding completion submit to prevent duplicate clicks. */
|
|
518
|
+
const onboardingFinishSavingRef = useRef(false);
|
|
519
|
+
// --- Confirm Modal ---
|
|
520
|
+
const { modalProps } = useConfirm();
|
|
521
|
+
const { prompt: promptModal, modalProps: promptModalProps } = usePrompt();
|
|
522
|
+
// ── Action notice ──────────────────────────────────────────────────
|
|
523
|
+
const setActionNotice = useCallback((text, tone = "info", ttlMs = 2800) => {
|
|
524
|
+
setActionNoticeState({ tone, text });
|
|
525
|
+
if (actionNoticeTimer.current != null) {
|
|
526
|
+
window.clearTimeout(actionNoticeTimer.current);
|
|
527
|
+
}
|
|
528
|
+
actionNoticeTimer.current = window.setTimeout(() => {
|
|
529
|
+
setActionNoticeState(null);
|
|
530
|
+
actionNoticeTimer.current = null;
|
|
531
|
+
}, ttlMs);
|
|
532
|
+
}, []);
|
|
533
|
+
const scheduleGreetingWave = useCallback((showOverlay = false) => {
|
|
534
|
+
if (typeof window === "undefined")
|
|
535
|
+
return;
|
|
536
|
+
if (greetingEmoteTimerRef.current != null) {
|
|
537
|
+
window.clearTimeout(greetingEmoteTimerRef.current);
|
|
538
|
+
}
|
|
539
|
+
greetingEmoteTimerRef.current = window.setTimeout(() => {
|
|
540
|
+
dispatchAppEmoteEvent({
|
|
541
|
+
...GREETING_WAVE_EMOTE,
|
|
542
|
+
showOverlay,
|
|
543
|
+
});
|
|
544
|
+
greetingEmoteTimerRef.current = null;
|
|
545
|
+
}, GREETING_EMOTE_DELAY_MS);
|
|
546
|
+
}, []);
|
|
547
|
+
const scheduleGreetingWaveForCompanion = useCallback((showOverlay = false) => {
|
|
548
|
+
if (uiShellMode !== "companion") {
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
scheduleGreetingWave(showOverlay);
|
|
552
|
+
}, [scheduleGreetingWave, uiShellMode]);
|
|
553
|
+
useEffect(() => {
|
|
554
|
+
return () => {
|
|
555
|
+
if (greetingEmoteTimerRef.current != null) {
|
|
556
|
+
window.clearTimeout(greetingEmoteTimerRef.current);
|
|
557
|
+
greetingEmoteTimerRef.current = null;
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
}, []);
|
|
561
|
+
// ── Clipboard ──────────────────────────────────────────────────────
|
|
562
|
+
const copyToClipboard = useCallback(async (text) => {
|
|
563
|
+
await copyTextToClipboard(text);
|
|
564
|
+
}, []);
|
|
565
|
+
// ── Language ────────────────────────────────────────────────────────
|
|
566
|
+
const setUiLanguage = useCallback((language) => {
|
|
567
|
+
const nextLanguage = normalizeLanguage(language);
|
|
568
|
+
setUiLanguageState(nextLanguage);
|
|
569
|
+
void client.updateConfig({ ui: { language: nextLanguage } }).catch(() => {
|
|
570
|
+
setActionNotice(translateText(nextLanguage, "settings.languageSyncFailed"), "error", 3200);
|
|
571
|
+
});
|
|
572
|
+
}, [setActionNotice]);
|
|
573
|
+
useEffect(() => {
|
|
574
|
+
saveUiLanguage(uiLanguage);
|
|
575
|
+
if (typeof client
|
|
576
|
+
.setUiLanguage === "function") {
|
|
577
|
+
client.setUiLanguage(uiLanguage);
|
|
578
|
+
}
|
|
579
|
+
}, [uiLanguage]);
|
|
580
|
+
const setUiShellMode = useCallback((mode) => {
|
|
581
|
+
setUiShellModeState(normalizeUiShellMode(mode));
|
|
582
|
+
}, []);
|
|
583
|
+
useEffect(() => {
|
|
584
|
+
saveUiShellMode(uiShellMode);
|
|
585
|
+
}, [uiShellMode]);
|
|
586
|
+
useEffect(() => {
|
|
587
|
+
saveLastNativeTab(lastNativeTab);
|
|
588
|
+
}, [lastNativeTab]);
|
|
589
|
+
// ── Theme ──────────────────────────────────────────────────────────
|
|
590
|
+
const setUiTheme = useCallback((theme) => {
|
|
591
|
+
setUiThemeState(normalizeUiTheme(theme));
|
|
592
|
+
}, []);
|
|
593
|
+
useEffect(() => {
|
|
594
|
+
saveUiTheme(uiTheme);
|
|
595
|
+
applyUiTheme(uiTheme);
|
|
596
|
+
}, [uiTheme]);
|
|
597
|
+
// Apply theme on initial mount
|
|
598
|
+
useEffect(() => {
|
|
599
|
+
applyUiTheme(uiTheme);
|
|
600
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
601
|
+
}, [uiTheme]);
|
|
602
|
+
// ── Navigation ─────────────────────────────────────────────────────
|
|
603
|
+
const setTab = useCallback((newTab) => {
|
|
604
|
+
setTabRaw(newTab);
|
|
605
|
+
if (newTab === "apps") {
|
|
606
|
+
setAppsSubTab(activeGameViewerUrl.trim() ? "games" : "browse");
|
|
607
|
+
}
|
|
608
|
+
const path = pathForTab(newTab);
|
|
609
|
+
// In Electron packaged builds (file:// URLs), use hash routing to avoid
|
|
610
|
+
// "Not allowed to load local resource: file:///..." errors.
|
|
611
|
+
if (window.location.protocol === "file:") {
|
|
612
|
+
window.location.hash = path;
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
window.history.pushState(null, "", path);
|
|
616
|
+
}
|
|
617
|
+
}, [activeGameViewerUrl]);
|
|
618
|
+
useEffect(() => {
|
|
619
|
+
const shouldRememberTab = uiShellMode === "native" &&
|
|
620
|
+
tab !== "companion" &&
|
|
621
|
+
tab !== "character" &&
|
|
622
|
+
tab !== "character-select";
|
|
623
|
+
if (!shouldRememberTab) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
setLastNativeTabState((prev) => (prev === tab ? prev : tab));
|
|
627
|
+
}, [tab, uiShellMode]);
|
|
628
|
+
const switchUiShellMode = useCallback((mode) => {
|
|
629
|
+
const nextMode = normalizeUiShellMode(mode);
|
|
630
|
+
if (nextMode === uiShellMode) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
if (nextMode === "native") {
|
|
634
|
+
setUiShellModeState("native");
|
|
635
|
+
setTab(lastNativeTab);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
setUiShellModeState("companion");
|
|
639
|
+
setTab("companion");
|
|
640
|
+
}, [lastNativeTab, setTab, uiShellMode]);
|
|
641
|
+
const switchShellView = useCallback((view) => {
|
|
642
|
+
if (view === "companion") {
|
|
643
|
+
setUiShellModeState("companion");
|
|
644
|
+
setTab("companion");
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
setUiShellModeState("native");
|
|
648
|
+
if (view === "character") {
|
|
649
|
+
setTab("character-select");
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
setTab(lastNativeTab);
|
|
653
|
+
}, [lastNativeTab, setTab]);
|
|
654
|
+
const sortTriggersByNextRun = useCallback((items) => {
|
|
655
|
+
return [...items].sort((a, b) => {
|
|
656
|
+
const aNext = a.nextRunAtMs ?? Number.MAX_SAFE_INTEGER;
|
|
657
|
+
const bNext = b.nextRunAtMs ?? Number.MAX_SAFE_INTEGER;
|
|
658
|
+
if (aNext !== bNext)
|
|
659
|
+
return aNext - bNext;
|
|
660
|
+
return a.displayName.localeCompare(b.displayName);
|
|
661
|
+
});
|
|
662
|
+
}, []);
|
|
663
|
+
// ── Data loading ───────────────────────────────────────────────────
|
|
664
|
+
const loadPlugins = useCallback(async () => {
|
|
665
|
+
try {
|
|
666
|
+
const { plugins: p } = await client.getPlugins();
|
|
667
|
+
setPlugins(p);
|
|
668
|
+
}
|
|
669
|
+
catch {
|
|
670
|
+
/* ignore */
|
|
671
|
+
}
|
|
672
|
+
}, []);
|
|
673
|
+
const loadSkills = useCallback(async () => {
|
|
674
|
+
try {
|
|
675
|
+
const { skills: s } = await client.getSkills();
|
|
676
|
+
setSkills(s);
|
|
677
|
+
}
|
|
678
|
+
catch {
|
|
679
|
+
/* ignore */
|
|
680
|
+
}
|
|
681
|
+
}, []);
|
|
682
|
+
const refreshSkills = useCallback(async () => {
|
|
683
|
+
try {
|
|
684
|
+
const { skills: s } = await client.refreshSkills();
|
|
685
|
+
setSkills(s);
|
|
686
|
+
}
|
|
687
|
+
catch {
|
|
688
|
+
try {
|
|
689
|
+
const { skills: s } = await client.getSkills();
|
|
690
|
+
setSkills(s);
|
|
691
|
+
}
|
|
692
|
+
catch {
|
|
693
|
+
/* ignore */
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}, []);
|
|
697
|
+
const loadLogs = useCallback(async () => {
|
|
698
|
+
try {
|
|
699
|
+
const filter = {};
|
|
700
|
+
if (logTagFilter)
|
|
701
|
+
filter.tag = logTagFilter;
|
|
702
|
+
if (logLevelFilter)
|
|
703
|
+
filter.level = logLevelFilter;
|
|
704
|
+
if (logSourceFilter)
|
|
705
|
+
filter.source = logSourceFilter;
|
|
706
|
+
const data = await client.getLogs(Object.keys(filter).length > 0 ? filter : undefined);
|
|
707
|
+
setLogs(data.entries);
|
|
708
|
+
if (data.sources?.length)
|
|
709
|
+
setLogSources(data.sources);
|
|
710
|
+
if (data.tags?.length)
|
|
711
|
+
setLogTags(data.tags);
|
|
712
|
+
}
|
|
713
|
+
catch {
|
|
714
|
+
/* ignore */
|
|
715
|
+
}
|
|
716
|
+
}, [logTagFilter, logLevelFilter, logSourceFilter]);
|
|
717
|
+
const loadTriggerHealth = useCallback(async () => {
|
|
718
|
+
try {
|
|
719
|
+
const health = await client.getTriggerHealth();
|
|
720
|
+
setTriggerHealth(health);
|
|
721
|
+
}
|
|
722
|
+
catch {
|
|
723
|
+
setTriggerHealth(null);
|
|
724
|
+
}
|
|
725
|
+
}, []);
|
|
726
|
+
const loadTriggers = useCallback(async () => {
|
|
727
|
+
setTriggersLoading(true);
|
|
728
|
+
try {
|
|
729
|
+
const data = await client.getTriggers();
|
|
730
|
+
setTriggers(sortTriggersByNextRun(data.triggers));
|
|
731
|
+
setTriggerError(null);
|
|
732
|
+
}
|
|
733
|
+
catch (err) {
|
|
734
|
+
const message = err instanceof Error ? err.message : "Failed to load triggers";
|
|
735
|
+
setTriggerError(message);
|
|
736
|
+
setTriggers([]);
|
|
737
|
+
}
|
|
738
|
+
finally {
|
|
739
|
+
setTriggersLoading(false);
|
|
740
|
+
}
|
|
741
|
+
}, [sortTriggersByNextRun]);
|
|
742
|
+
const loadTriggerRuns = useCallback(async (id) => {
|
|
743
|
+
try {
|
|
744
|
+
const data = await client.getTriggerRuns(id);
|
|
745
|
+
setTriggerRunsById((prev) => ({
|
|
746
|
+
...prev,
|
|
747
|
+
[id]: data.runs,
|
|
748
|
+
}));
|
|
749
|
+
setTriggerError(null);
|
|
750
|
+
}
|
|
751
|
+
catch (err) {
|
|
752
|
+
const message = err instanceof Error ? err.message : "Failed to load trigger runs";
|
|
753
|
+
setTriggerError(message);
|
|
754
|
+
}
|
|
755
|
+
}, []);
|
|
756
|
+
const createTrigger = useCallback(async (request) => {
|
|
757
|
+
setTriggersSaving(true);
|
|
758
|
+
try {
|
|
759
|
+
const response = await client.createTrigger(request);
|
|
760
|
+
const created = response.trigger;
|
|
761
|
+
setTriggers((prev) => {
|
|
762
|
+
const merged = prev.filter((item) => item.id !== created.id);
|
|
763
|
+
merged.push(created);
|
|
764
|
+
return sortTriggersByNextRun(merged);
|
|
765
|
+
});
|
|
766
|
+
setTriggerError(null);
|
|
767
|
+
void loadTriggerHealth();
|
|
768
|
+
return created;
|
|
769
|
+
}
|
|
770
|
+
catch (err) {
|
|
771
|
+
const message = err instanceof Error ? err.message : "Failed to create trigger";
|
|
772
|
+
setTriggerError(message);
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
finally {
|
|
776
|
+
setTriggersSaving(false);
|
|
777
|
+
}
|
|
778
|
+
}, [loadTriggerHealth, sortTriggersByNextRun]);
|
|
779
|
+
const updateTrigger = useCallback(async (id, request) => {
|
|
780
|
+
setTriggersSaving(true);
|
|
781
|
+
try {
|
|
782
|
+
const response = await client.updateTrigger(id, request);
|
|
783
|
+
const updated = response.trigger;
|
|
784
|
+
setTriggers((prev) => {
|
|
785
|
+
const merged = prev.map((item) => item.id === updated.id ? updated : item);
|
|
786
|
+
return sortTriggersByNextRun(merged);
|
|
787
|
+
});
|
|
788
|
+
setTriggerError(null);
|
|
789
|
+
void loadTriggerHealth();
|
|
790
|
+
return updated;
|
|
791
|
+
}
|
|
792
|
+
catch (err) {
|
|
793
|
+
const message = err instanceof Error ? err.message : "Failed to update trigger";
|
|
794
|
+
setTriggerError(message);
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
finally {
|
|
798
|
+
setTriggersSaving(false);
|
|
799
|
+
}
|
|
800
|
+
}, [loadTriggerHealth, sortTriggersByNextRun]);
|
|
801
|
+
const deleteTrigger = useCallback(async (id) => {
|
|
802
|
+
setTriggersSaving(true);
|
|
803
|
+
try {
|
|
804
|
+
await client.deleteTrigger(id);
|
|
805
|
+
setTriggers((prev) => prev.filter((item) => item.id !== id));
|
|
806
|
+
setTriggerRunsById((prev) => {
|
|
807
|
+
const next = {};
|
|
808
|
+
for (const [key, runs] of Object.entries(prev)) {
|
|
809
|
+
if (key !== id)
|
|
810
|
+
next[key] = runs;
|
|
811
|
+
}
|
|
812
|
+
return next;
|
|
813
|
+
});
|
|
814
|
+
setTriggerError(null);
|
|
815
|
+
void loadTriggerHealth();
|
|
816
|
+
return true;
|
|
817
|
+
}
|
|
818
|
+
catch (err) {
|
|
819
|
+
const message = err instanceof Error ? err.message : "Failed to delete trigger";
|
|
820
|
+
setTriggerError(message);
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
finally {
|
|
824
|
+
setTriggersSaving(false);
|
|
825
|
+
}
|
|
826
|
+
}, [loadTriggerHealth]);
|
|
827
|
+
const runTriggerNow = useCallback(async (id) => {
|
|
828
|
+
setTriggersSaving(true);
|
|
829
|
+
try {
|
|
830
|
+
const response = await client.runTriggerNow(id);
|
|
831
|
+
if (response.trigger) {
|
|
832
|
+
const trigger = response.trigger;
|
|
833
|
+
setTriggers((prev) => {
|
|
834
|
+
const idx = prev.findIndex((item) => item.id === id);
|
|
835
|
+
if (idx === -1) {
|
|
836
|
+
return sortTriggersByNextRun([...prev, trigger]);
|
|
837
|
+
}
|
|
838
|
+
const updated = [...prev];
|
|
839
|
+
updated[idx] = trigger;
|
|
840
|
+
return sortTriggersByNextRun(updated);
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
await loadTriggers();
|
|
845
|
+
}
|
|
846
|
+
await loadTriggerRuns(id);
|
|
847
|
+
void loadTriggerHealth();
|
|
848
|
+
setTriggerError(null);
|
|
849
|
+
return response.ok;
|
|
850
|
+
}
|
|
851
|
+
catch (err) {
|
|
852
|
+
const message = err instanceof Error ? err.message : "Failed to run trigger";
|
|
853
|
+
setTriggerError(message);
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
finally {
|
|
857
|
+
setTriggersSaving(false);
|
|
858
|
+
}
|
|
859
|
+
}, [loadTriggerHealth, loadTriggerRuns, loadTriggers, sortTriggersByNextRun]);
|
|
860
|
+
const applyAutonomyEventMerge = useCallback((incomingEvents, replay = false) => {
|
|
861
|
+
const merged = mergeAutonomyEvents({
|
|
862
|
+
store: autonomousStoreRef.current,
|
|
863
|
+
incomingEvents,
|
|
864
|
+
runHealthByRunId: autonomousRunHealthByRunIdRef.current,
|
|
865
|
+
replay,
|
|
866
|
+
});
|
|
867
|
+
autonomousStoreRef.current = merged.store;
|
|
868
|
+
autonomousEventsRef.current = merged.events;
|
|
869
|
+
autonomousLatestEventIdRef.current = merged.latestEventId;
|
|
870
|
+
autonomousRunHealthByRunIdRef.current = merged.runHealthByRunId;
|
|
871
|
+
setAutonomousEvents(merged.events);
|
|
872
|
+
setAutonomousLatestEventId(merged.latestEventId);
|
|
873
|
+
setAutonomousRunHealthByRunId(merged.runHealthByRunId);
|
|
874
|
+
return merged;
|
|
875
|
+
}, []);
|
|
876
|
+
const fetchAutonomyReplay = useCallback(async () => {
|
|
877
|
+
if (autonomousReplayInFlightRef.current)
|
|
878
|
+
return;
|
|
879
|
+
autonomousReplayInFlightRef.current = true;
|
|
880
|
+
try {
|
|
881
|
+
const afterEventId = autonomousStoreRef.current.watermark ?? undefined;
|
|
882
|
+
const replay = await client.getAgentEvents({
|
|
883
|
+
afterEventId,
|
|
884
|
+
limit: 300,
|
|
885
|
+
});
|
|
886
|
+
if (replay.events.length > 0) {
|
|
887
|
+
applyAutonomyEventMerge(replay.events);
|
|
888
|
+
}
|
|
889
|
+
const gapReplays = buildAutonomyGapReplayRequests(autonomousRunHealthByRunIdRef.current, autonomousStoreRef.current).slice(0, 4);
|
|
890
|
+
for (const request of gapReplays) {
|
|
891
|
+
const gapReplay = await client.getAgentEvents({
|
|
892
|
+
runId: request.runId,
|
|
893
|
+
fromSeq: request.fromSeq,
|
|
894
|
+
limit: 300,
|
|
895
|
+
});
|
|
896
|
+
if (gapReplay.events.length > 0) {
|
|
897
|
+
applyAutonomyEventMerge(gapReplay.events);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
if (hasPendingAutonomyGaps(autonomousRunHealthByRunIdRef.current)) {
|
|
901
|
+
const partial = markPendingAutonomyGapsPartial(autonomousRunHealthByRunIdRef.current, Date.now());
|
|
902
|
+
autonomousRunHealthByRunIdRef.current = partial;
|
|
903
|
+
setAutonomousRunHealthByRunId(partial);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
catch (err) {
|
|
907
|
+
if (hasPendingAutonomyGaps(autonomousRunHealthByRunIdRef.current)) {
|
|
908
|
+
const partial = markPendingAutonomyGapsPartial(autonomousRunHealthByRunIdRef.current, Date.now());
|
|
909
|
+
autonomousRunHealthByRunIdRef.current = partial;
|
|
910
|
+
setAutonomousRunHealthByRunId(partial);
|
|
911
|
+
}
|
|
912
|
+
console.warn("[milady] Failed to fetch autonomous event replay", err);
|
|
913
|
+
}
|
|
914
|
+
finally {
|
|
915
|
+
autonomousReplayInFlightRef.current = false;
|
|
916
|
+
}
|
|
917
|
+
}, [applyAutonomyEventMerge]);
|
|
918
|
+
const appendAutonomousEvent = useCallback((event) => {
|
|
919
|
+
const merged = applyAutonomyEventMerge([event]);
|
|
920
|
+
if (merged.runsWithNewGaps.length > 0) {
|
|
921
|
+
void fetchAutonomyReplay();
|
|
922
|
+
}
|
|
923
|
+
}, [applyAutonomyEventMerge, fetchAutonomyReplay]);
|
|
924
|
+
const loadConversations = useCallback(async () => {
|
|
925
|
+
try {
|
|
926
|
+
const { conversations: c } = await client.listConversations();
|
|
927
|
+
setConversations(c);
|
|
928
|
+
return c;
|
|
929
|
+
}
|
|
930
|
+
catch {
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
}, []);
|
|
934
|
+
const loadConversationMessages = useCallback(async (convId) => {
|
|
935
|
+
try {
|
|
936
|
+
const { messages } = await client.getConversationMessages(convId);
|
|
937
|
+
const nextMessages = filterRenderableConversationMessages(messages);
|
|
938
|
+
greetingFiredRef.current = nextMessages.length > 0;
|
|
939
|
+
conversationMessagesRef.current = nextMessages;
|
|
940
|
+
setConversationMessages(nextMessages);
|
|
941
|
+
return { ok: true };
|
|
942
|
+
}
|
|
943
|
+
catch (err) {
|
|
944
|
+
const status = err.status;
|
|
945
|
+
if (status === 404) {
|
|
946
|
+
const refreshed = await client.listConversations().catch(() => null);
|
|
947
|
+
if (refreshed) {
|
|
948
|
+
setConversations(refreshed.conversations);
|
|
949
|
+
if (activeConversationIdRef.current === convId) {
|
|
950
|
+
const fallbackId = refreshed.conversations[0]?.id ?? null;
|
|
951
|
+
setActiveConversationId(fallbackId);
|
|
952
|
+
activeConversationIdRef.current = fallbackId;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
else if (activeConversationIdRef.current === convId) {
|
|
956
|
+
setActiveConversationId(null);
|
|
957
|
+
activeConversationIdRef.current = null;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
greetingFiredRef.current = false;
|
|
961
|
+
conversationMessagesRef.current = [];
|
|
962
|
+
setConversationMessages([]);
|
|
963
|
+
return {
|
|
964
|
+
ok: false,
|
|
965
|
+
status,
|
|
966
|
+
message: err instanceof Error
|
|
967
|
+
? err.message
|
|
968
|
+
: "Failed to load conversation messages",
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
}, []);
|
|
972
|
+
const loadWalletConfig = useCallback(async () => {
|
|
973
|
+
try {
|
|
974
|
+
const cfg = await client.getWalletConfig();
|
|
975
|
+
setWalletConfig(cfg);
|
|
976
|
+
setWalletAddresses({
|
|
977
|
+
evmAddress: cfg.evmAddress,
|
|
978
|
+
solanaAddress: cfg.solanaAddress,
|
|
979
|
+
});
|
|
980
|
+
setWalletError(null);
|
|
981
|
+
}
|
|
982
|
+
catch (err) {
|
|
983
|
+
setWalletError(`Failed to load wallet config: ${err instanceof Error ? err.message : "network error"}`);
|
|
984
|
+
}
|
|
985
|
+
}, []);
|
|
986
|
+
const loadBalances = useCallback(async () => {
|
|
987
|
+
setWalletLoading(true);
|
|
988
|
+
setWalletError(null);
|
|
989
|
+
try {
|
|
990
|
+
const b = await client.getWalletBalances();
|
|
991
|
+
setWalletBalances(b);
|
|
992
|
+
}
|
|
993
|
+
catch (err) {
|
|
994
|
+
setWalletError(`Failed to fetch balances: ${err instanceof Error ? err.message : "network error"}`);
|
|
995
|
+
}
|
|
996
|
+
setWalletLoading(false);
|
|
997
|
+
}, []);
|
|
998
|
+
const loadNfts = useCallback(async () => {
|
|
999
|
+
setWalletNftsLoading(true);
|
|
1000
|
+
setWalletError(null);
|
|
1001
|
+
try {
|
|
1002
|
+
const n = await client.getWalletNfts();
|
|
1003
|
+
setWalletNfts(n);
|
|
1004
|
+
}
|
|
1005
|
+
catch (err) {
|
|
1006
|
+
setWalletError(`Failed to fetch NFTs: ${err instanceof Error ? err.message : "network error"}`);
|
|
1007
|
+
}
|
|
1008
|
+
setWalletNftsLoading(false);
|
|
1009
|
+
}, []);
|
|
1010
|
+
const getBscTradePreflight = useCallback(async (tokenAddress) => client.getBscTradePreflight(tokenAddress), []);
|
|
1011
|
+
const getBscTradeQuote = useCallback(async (request) => client.getBscTradeQuote(request), []);
|
|
1012
|
+
const getBscTradeTxStatus = useCallback(async (hash) => client.getBscTradeTxStatus(hash), []);
|
|
1013
|
+
const loadWalletTradingProfile = useCallback(async (window = "30d", source = "all") => client.getWalletTradingProfile(window, source), []);
|
|
1014
|
+
const executeBscTrade = useCallback(async (request) => client.executeBscTrade(request), []);
|
|
1015
|
+
const executeBscTransfer = useCallback(async (request) => client.executeBscTransfer(request), []);
|
|
1016
|
+
const loadInventory = useCallback(async () => {
|
|
1017
|
+
await loadWalletConfig();
|
|
1018
|
+
}, [loadWalletConfig]);
|
|
1019
|
+
const loadCharacter = useCallback(async () => {
|
|
1020
|
+
setCharacterLoading(true);
|
|
1021
|
+
setCharacterSaveError(null);
|
|
1022
|
+
setCharacterSaveSuccess(null);
|
|
1023
|
+
try {
|
|
1024
|
+
const { character } = await client.getCharacter();
|
|
1025
|
+
setCharacterData(character);
|
|
1026
|
+
setCharacterDraft({
|
|
1027
|
+
name: character.name ?? "",
|
|
1028
|
+
username: character.username ?? "",
|
|
1029
|
+
bio: Array.isArray(character.bio)
|
|
1030
|
+
? character.bio.join("\n")
|
|
1031
|
+
: (character.bio ?? ""),
|
|
1032
|
+
system: character.system ?? "",
|
|
1033
|
+
adjectives: character.adjectives ?? [],
|
|
1034
|
+
style: {
|
|
1035
|
+
all: character.style?.all ?? [],
|
|
1036
|
+
chat: character.style?.chat ?? [],
|
|
1037
|
+
post: character.style?.post ?? [],
|
|
1038
|
+
},
|
|
1039
|
+
messageExamples: character.messageExamples ?? [],
|
|
1040
|
+
postExamples: character.postExamples ?? [],
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
catch {
|
|
1044
|
+
setCharacterData(null);
|
|
1045
|
+
setCharacterDraft({});
|
|
1046
|
+
}
|
|
1047
|
+
setCharacterLoading(false);
|
|
1048
|
+
}, []);
|
|
1049
|
+
const loadWorkbench = useCallback(async () => {
|
|
1050
|
+
setWorkbenchLoading(true);
|
|
1051
|
+
try {
|
|
1052
|
+
const result = await client.getWorkbenchOverview();
|
|
1053
|
+
setWorkbench(result);
|
|
1054
|
+
setWorkbenchTasksAvailable(result.tasksAvailable ?? false);
|
|
1055
|
+
setWorkbenchTriggersAvailable(result.triggersAvailable ?? false);
|
|
1056
|
+
setWorkbenchTodosAvailable(result.todosAvailable ?? false);
|
|
1057
|
+
}
|
|
1058
|
+
catch {
|
|
1059
|
+
setWorkbench(null);
|
|
1060
|
+
setWorkbenchTasksAvailable(false);
|
|
1061
|
+
setWorkbenchTriggersAvailable(false);
|
|
1062
|
+
setWorkbenchTodosAvailable(false);
|
|
1063
|
+
}
|
|
1064
|
+
finally {
|
|
1065
|
+
setWorkbenchLoading(false);
|
|
1066
|
+
}
|
|
1067
|
+
}, []);
|
|
1068
|
+
const loadUpdateStatus = useCallback(async (force = false) => {
|
|
1069
|
+
setUpdateLoading(true);
|
|
1070
|
+
try {
|
|
1071
|
+
const status = await client.getUpdateStatus(force);
|
|
1072
|
+
setUpdateStatus(status);
|
|
1073
|
+
}
|
|
1074
|
+
catch {
|
|
1075
|
+
/* ignore */
|
|
1076
|
+
}
|
|
1077
|
+
setUpdateLoading(false);
|
|
1078
|
+
}, []);
|
|
1079
|
+
const checkExtensionStatus = useCallback(async () => {
|
|
1080
|
+
setExtensionChecking(true);
|
|
1081
|
+
try {
|
|
1082
|
+
const ext = await client.getExtensionStatus();
|
|
1083
|
+
setExtensionStatus(ext);
|
|
1084
|
+
}
|
|
1085
|
+
catch {
|
|
1086
|
+
setExtensionStatus({
|
|
1087
|
+
relayReachable: false,
|
|
1088
|
+
relayPort: 18792,
|
|
1089
|
+
extensionPath: null,
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
setExtensionChecking(false);
|
|
1093
|
+
}, []);
|
|
1094
|
+
const pollCloudCredits = useCallback(async () => {
|
|
1095
|
+
const cloudStatus = await client.getCloudStatus().catch(() => null);
|
|
1096
|
+
if (!cloudStatus) {
|
|
1097
|
+
setElizaCloudConnected(false);
|
|
1098
|
+
setElizaCloudCredits(null);
|
|
1099
|
+
setElizaCloudCreditsLow(false);
|
|
1100
|
+
setElizaCloudCreditsCritical(false);
|
|
1101
|
+
return false;
|
|
1102
|
+
}
|
|
1103
|
+
// A cached cloud API key represents a completed login and should be shared
|
|
1104
|
+
// across all views, even before runtime CLOUD_AUTH fully initializes.
|
|
1105
|
+
const isConnected = Boolean(cloudStatus.connected || cloudStatus.hasApiKey);
|
|
1106
|
+
setElizaCloudEnabled(Boolean(cloudStatus.enabled ?? false));
|
|
1107
|
+
setElizaCloudConnected(isConnected);
|
|
1108
|
+
setElizaCloudUserId(cloudStatus.userId ?? null);
|
|
1109
|
+
if (cloudStatus.topUpUrl)
|
|
1110
|
+
setElizaCloudTopUpUrl(cloudStatus.topUpUrl);
|
|
1111
|
+
if (isConnected) {
|
|
1112
|
+
const credits = await client.getCloudCredits().catch(() => null);
|
|
1113
|
+
if (credits && typeof credits.balance === "number") {
|
|
1114
|
+
setElizaCloudCredits(credits.balance);
|
|
1115
|
+
setElizaCloudCreditsLow(credits.low ?? false);
|
|
1116
|
+
setElizaCloudCreditsCritical(credits.critical ?? false);
|
|
1117
|
+
if (credits.topUpUrl)
|
|
1118
|
+
setElizaCloudTopUpUrl(credits.topUpUrl);
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
setElizaCloudCredits(null);
|
|
1122
|
+
setElizaCloudCreditsLow(false);
|
|
1123
|
+
setElizaCloudCreditsCritical(false);
|
|
1124
|
+
if (credits?.topUpUrl)
|
|
1125
|
+
setElizaCloudTopUpUrl(credits.topUpUrl);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
setElizaCloudCredits(null);
|
|
1130
|
+
setElizaCloudCreditsLow(false);
|
|
1131
|
+
setElizaCloudCreditsCritical(false);
|
|
1132
|
+
}
|
|
1133
|
+
return isConnected;
|
|
1134
|
+
}, []);
|
|
1135
|
+
// ── Lifecycle actions ──────────────────────────────────────────────
|
|
1136
|
+
const beginLifecycleAction = useCallback((action) => {
|
|
1137
|
+
if (lifecycleBusyRef.current) {
|
|
1138
|
+
const activeAction = lifecycleActionRef.current ?? lifecycleAction ?? action;
|
|
1139
|
+
setActionNotice(`Agent action already in progress (${LIFECYCLE_MESSAGES[activeAction].inProgress}). Please wait.`, "info", 2800);
|
|
1140
|
+
return false;
|
|
1141
|
+
}
|
|
1142
|
+
lifecycleBusyRef.current = true;
|
|
1143
|
+
lifecycleActionRef.current = action;
|
|
1144
|
+
setLifecycleBusy(true);
|
|
1145
|
+
setLifecycleAction(action);
|
|
1146
|
+
return true;
|
|
1147
|
+
}, [lifecycleAction, setActionNotice]);
|
|
1148
|
+
const finishLifecycleAction = useCallback(() => {
|
|
1149
|
+
lifecycleBusyRef.current = false;
|
|
1150
|
+
lifecycleActionRef.current = null;
|
|
1151
|
+
setLifecycleBusy(false);
|
|
1152
|
+
setLifecycleAction(null);
|
|
1153
|
+
}, []);
|
|
1154
|
+
// ── Chat ───────────────────────────────────────────────────────────
|
|
1155
|
+
/** Request an agent greeting for a conversation and add it to messages. */
|
|
1156
|
+
const fetchGreeting = useCallback(async (convId, options) => {
|
|
1157
|
+
if (greetingInFlightConversationRef.current === convId) {
|
|
1158
|
+
return false;
|
|
1159
|
+
}
|
|
1160
|
+
greetingInFlightConversationRef.current = convId;
|
|
1161
|
+
setChatSending(true);
|
|
1162
|
+
try {
|
|
1163
|
+
const data = await client.requestGreeting(convId, uiLanguage);
|
|
1164
|
+
if (data.text) {
|
|
1165
|
+
greetingFiredRef.current = true;
|
|
1166
|
+
if (data.persisted === true) {
|
|
1167
|
+
scheduleGreetingWaveForCompanion(options?.showOverlay === true);
|
|
1168
|
+
}
|
|
1169
|
+
if (activeConversationIdRef.current === convId) {
|
|
1170
|
+
setConversationMessages((prev) => {
|
|
1171
|
+
if (prev.some((message) => message.role === "assistant" &&
|
|
1172
|
+
message.source === "agent_greeting" &&
|
|
1173
|
+
message.text === data.text)) {
|
|
1174
|
+
return prev;
|
|
1175
|
+
}
|
|
1176
|
+
return [
|
|
1177
|
+
...prev,
|
|
1178
|
+
{
|
|
1179
|
+
id: `greeting-${Date.now()}`,
|
|
1180
|
+
role: "assistant",
|
|
1181
|
+
text: data.text,
|
|
1182
|
+
timestamp: Date.now(),
|
|
1183
|
+
source: "agent_greeting",
|
|
1184
|
+
},
|
|
1185
|
+
];
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
return true;
|
|
1189
|
+
}
|
|
1190
|
+
greetingFiredRef.current = false;
|
|
1191
|
+
}
|
|
1192
|
+
catch {
|
|
1193
|
+
greetingFiredRef.current = false;
|
|
1194
|
+
/* greeting failed silently — user can still chat */
|
|
1195
|
+
}
|
|
1196
|
+
finally {
|
|
1197
|
+
if (greetingInFlightConversationRef.current === convId) {
|
|
1198
|
+
greetingInFlightConversationRef.current = null;
|
|
1199
|
+
}
|
|
1200
|
+
setChatSending(false);
|
|
1201
|
+
}
|
|
1202
|
+
return false;
|
|
1203
|
+
}, [scheduleGreetingWaveForCompanion, uiLanguage]);
|
|
1204
|
+
const requestGreetingWhenRunning = useCallback(async (convId, options) => {
|
|
1205
|
+
if (!convId || greetingFiredRef.current) {
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
try {
|
|
1209
|
+
const status = await client.getStatus();
|
|
1210
|
+
if (status.state === "running" && !greetingFiredRef.current) {
|
|
1211
|
+
await fetchGreeting(convId, options);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
catch (err) {
|
|
1215
|
+
console.warn("[milady][chat:init] failed to confirm runtime state for greeting", err);
|
|
1216
|
+
}
|
|
1217
|
+
}, [fetchGreeting]);
|
|
1218
|
+
const hydrateInitialConversationState = useCallback(async () => {
|
|
1219
|
+
const hydrationEpoch = ++conversationHydrationEpochRef.current;
|
|
1220
|
+
const isCurrentHydration = () => conversationHydrationEpochRef.current === hydrationEpoch;
|
|
1221
|
+
try {
|
|
1222
|
+
const { conversations: c } = await client.listConversations();
|
|
1223
|
+
if (!isCurrentHydration()) {
|
|
1224
|
+
return null;
|
|
1225
|
+
}
|
|
1226
|
+
setConversations(c);
|
|
1227
|
+
if (c.length > 0) {
|
|
1228
|
+
const savedConversationId = loadActiveConversationId();
|
|
1229
|
+
const restoredConversation = c.find((conversation) => conversation.id === savedConversationId) ??
|
|
1230
|
+
c[0];
|
|
1231
|
+
if (!isCurrentHydration()) {
|
|
1232
|
+
return null;
|
|
1233
|
+
}
|
|
1234
|
+
setActiveConversationId(restoredConversation.id);
|
|
1235
|
+
activeConversationIdRef.current = restoredConversation.id;
|
|
1236
|
+
client.sendWsMessage({
|
|
1237
|
+
type: "active-conversation",
|
|
1238
|
+
conversationId: restoredConversation.id,
|
|
1239
|
+
});
|
|
1240
|
+
try {
|
|
1241
|
+
const { messages } = await client.getConversationMessages(restoredConversation.id);
|
|
1242
|
+
if (!isCurrentHydration()) {
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
const nextMessages = filterRenderableConversationMessages(messages);
|
|
1246
|
+
greetingFiredRef.current = nextMessages.length > 0;
|
|
1247
|
+
conversationMessagesRef.current = nextMessages;
|
|
1248
|
+
setConversationMessages(nextMessages);
|
|
1249
|
+
return nextMessages.length === 0 ? restoredConversation.id : null;
|
|
1250
|
+
}
|
|
1251
|
+
catch (err) {
|
|
1252
|
+
if (!isCurrentHydration()) {
|
|
1253
|
+
return null;
|
|
1254
|
+
}
|
|
1255
|
+
console.warn("[milady][chat:init] failed to load restored conversation messages", err);
|
|
1256
|
+
greetingFiredRef.current = false;
|
|
1257
|
+
conversationMessagesRef.current = [];
|
|
1258
|
+
setConversationMessages([]);
|
|
1259
|
+
return restoredConversation.id;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
const { conversation, greeting } = await client.createConversation(translateText(uiLanguage, "conversations.newChatTitle"), {
|
|
1263
|
+
bootstrapGreeting: true,
|
|
1264
|
+
lang: uiLanguage,
|
|
1265
|
+
});
|
|
1266
|
+
if (!isCurrentHydration()) {
|
|
1267
|
+
return null;
|
|
1268
|
+
}
|
|
1269
|
+
const nextCutoffTs = Date.now();
|
|
1270
|
+
setConversations([conversation]);
|
|
1271
|
+
setActiveConversationId(conversation.id);
|
|
1272
|
+
activeConversationIdRef.current = conversation.id;
|
|
1273
|
+
setCompanionMessageCutoffTs(nextCutoffTs);
|
|
1274
|
+
client.sendWsMessage({
|
|
1275
|
+
type: "active-conversation",
|
|
1276
|
+
conversationId: conversation.id,
|
|
1277
|
+
});
|
|
1278
|
+
const greetingText = greeting?.text?.trim() ?? "";
|
|
1279
|
+
if (greetingText) {
|
|
1280
|
+
greetingFiredRef.current = true;
|
|
1281
|
+
if (greeting?.persisted === true) {
|
|
1282
|
+
scheduleGreetingWaveForCompanion(true);
|
|
1283
|
+
}
|
|
1284
|
+
const nextMessages = [
|
|
1285
|
+
{
|
|
1286
|
+
id: `greeting-${Date.now()}`,
|
|
1287
|
+
role: "assistant",
|
|
1288
|
+
text: greetingText,
|
|
1289
|
+
timestamp: Date.now(),
|
|
1290
|
+
source: "agent_greeting",
|
|
1291
|
+
},
|
|
1292
|
+
];
|
|
1293
|
+
conversationMessagesRef.current = nextMessages;
|
|
1294
|
+
setConversationMessages(nextMessages);
|
|
1295
|
+
return null;
|
|
1296
|
+
}
|
|
1297
|
+
greetingFiredRef.current = false;
|
|
1298
|
+
conversationMessagesRef.current = [];
|
|
1299
|
+
setConversationMessages([]);
|
|
1300
|
+
return conversation.id;
|
|
1301
|
+
}
|
|
1302
|
+
catch (err) {
|
|
1303
|
+
console.warn("[milady][chat:init] failed to hydrate conversations", err);
|
|
1304
|
+
return null;
|
|
1305
|
+
}
|
|
1306
|
+
}, [scheduleGreetingWaveForCompanion, uiLanguage]);
|
|
1307
|
+
const handleStart = useCallback(async () => {
|
|
1308
|
+
if (!beginLifecycleAction("start"))
|
|
1309
|
+
return;
|
|
1310
|
+
setActionNotice(LIFECYCLE_MESSAGES.start.progress, "info", 3000);
|
|
1311
|
+
try {
|
|
1312
|
+
const s = await client.startAgent();
|
|
1313
|
+
setAgentStatus(s);
|
|
1314
|
+
setActionNotice(LIFECYCLE_MESSAGES.start.success, "success", 2400);
|
|
1315
|
+
}
|
|
1316
|
+
catch (err) {
|
|
1317
|
+
setActionNotice(`Failed to ${LIFECYCLE_MESSAGES.start.verb} agent: ${err instanceof Error ? err.message : "unknown error"}`, "error", 4200);
|
|
1318
|
+
}
|
|
1319
|
+
finally {
|
|
1320
|
+
finishLifecycleAction();
|
|
1321
|
+
}
|
|
1322
|
+
}, [beginLifecycleAction, finishLifecycleAction, setActionNotice]);
|
|
1323
|
+
const handleStop = useCallback(async () => {
|
|
1324
|
+
if (!beginLifecycleAction("stop"))
|
|
1325
|
+
return;
|
|
1326
|
+
setActionNotice(LIFECYCLE_MESSAGES.stop.progress, "info", 3000);
|
|
1327
|
+
try {
|
|
1328
|
+
const s = await client.stopAgent();
|
|
1329
|
+
setAgentStatus(s);
|
|
1330
|
+
setActionNotice(LIFECYCLE_MESSAGES.stop.success, "success", 2400);
|
|
1331
|
+
}
|
|
1332
|
+
catch (err) {
|
|
1333
|
+
setActionNotice(`Failed to ${LIFECYCLE_MESSAGES.stop.verb} agent: ${err instanceof Error ? err.message : "unknown error"}`, "error", 4200);
|
|
1334
|
+
}
|
|
1335
|
+
finally {
|
|
1336
|
+
finishLifecycleAction();
|
|
1337
|
+
}
|
|
1338
|
+
}, [beginLifecycleAction, finishLifecycleAction, setActionNotice]);
|
|
1339
|
+
const handleRestart = useCallback(async () => {
|
|
1340
|
+
if (!beginLifecycleAction("restart"))
|
|
1341
|
+
return;
|
|
1342
|
+
setActionNotice(LIFECYCLE_MESSAGES.restart.progress, "info", 3200);
|
|
1343
|
+
try {
|
|
1344
|
+
setAgentStatus({
|
|
1345
|
+
...(agentStatus ?? {
|
|
1346
|
+
agentName: "Milady",
|
|
1347
|
+
model: undefined,
|
|
1348
|
+
uptime: undefined,
|
|
1349
|
+
startedAt: undefined,
|
|
1350
|
+
}),
|
|
1351
|
+
state: "restarting",
|
|
1352
|
+
});
|
|
1353
|
+
// Server restart clears in-memory conversations — reset client state
|
|
1354
|
+
setActiveConversationId(null);
|
|
1355
|
+
setConversationMessages([]);
|
|
1356
|
+
setConversations([]);
|
|
1357
|
+
const s = await client.restartAgent();
|
|
1358
|
+
setAgentStatus(s);
|
|
1359
|
+
const greetConvId = await hydrateInitialConversationState();
|
|
1360
|
+
await requestGreetingWhenRunning(greetConvId, { showOverlay: true });
|
|
1361
|
+
setPendingRestart(false);
|
|
1362
|
+
setPendingRestartReasons([]);
|
|
1363
|
+
void loadPlugins();
|
|
1364
|
+
setActionNotice(LIFECYCLE_MESSAGES.restart.success, "success", 2400);
|
|
1365
|
+
}
|
|
1366
|
+
catch (err) {
|
|
1367
|
+
setActionNotice(`Failed to ${LIFECYCLE_MESSAGES.restart.verb} agent: ${err instanceof Error ? err.message : "unknown error"}`, "error", 4200);
|
|
1368
|
+
setTimeout(async () => {
|
|
1369
|
+
try {
|
|
1370
|
+
setAgentStatus(await client.getStatus());
|
|
1371
|
+
}
|
|
1372
|
+
catch {
|
|
1373
|
+
/* ignore */
|
|
1374
|
+
}
|
|
1375
|
+
}, 3000);
|
|
1376
|
+
}
|
|
1377
|
+
finally {
|
|
1378
|
+
finishLifecycleAction();
|
|
1379
|
+
}
|
|
1380
|
+
}, [
|
|
1381
|
+
agentStatus,
|
|
1382
|
+
beginLifecycleAction,
|
|
1383
|
+
finishLifecycleAction,
|
|
1384
|
+
setActionNotice,
|
|
1385
|
+
hydrateInitialConversationState,
|
|
1386
|
+
loadPlugins,
|
|
1387
|
+
requestGreetingWhenRunning,
|
|
1388
|
+
]);
|
|
1389
|
+
const dismissRestartBanner = useCallback(() => {
|
|
1390
|
+
setRestartBannerDismissed(true);
|
|
1391
|
+
}, []);
|
|
1392
|
+
const triggerRestart = useCallback(async () => {
|
|
1393
|
+
await handleRestart();
|
|
1394
|
+
}, [handleRestart]);
|
|
1395
|
+
// Backend disconnection banner actions
|
|
1396
|
+
const dismissBackendDisconnectedBanner = useCallback(() => {
|
|
1397
|
+
setBackendDisconnectedBannerDismissed(true);
|
|
1398
|
+
}, []);
|
|
1399
|
+
const retryBackendConnection = useCallback(() => {
|
|
1400
|
+
setBackendDisconnectedBannerDismissed(false);
|
|
1401
|
+
client.resetConnection();
|
|
1402
|
+
}, []);
|
|
1403
|
+
const dismissSystemWarning = useCallback((message) => {
|
|
1404
|
+
setSystemWarnings((prev) => prev.filter((m) => m !== message));
|
|
1405
|
+
}, []);
|
|
1406
|
+
const restartBackend = useCallback(async () => {
|
|
1407
|
+
const restarted = await invokeDesktopBridgeRequest({
|
|
1408
|
+
rpcMethod: "agentRestart",
|
|
1409
|
+
ipcChannel: "agent:restart",
|
|
1410
|
+
});
|
|
1411
|
+
if (restarted === null) {
|
|
1412
|
+
// Fallback for web: call API restart endpoint
|
|
1413
|
+
await client.restart();
|
|
1414
|
+
}
|
|
1415
|
+
// Reset connection state after restart
|
|
1416
|
+
setBackendConnection((prev) => ({
|
|
1417
|
+
...prev,
|
|
1418
|
+
state: "disconnected",
|
|
1419
|
+
reconnectAttempt: 0,
|
|
1420
|
+
showDisconnectedUI: false,
|
|
1421
|
+
}));
|
|
1422
|
+
setBackendDisconnectedBannerDismissed(false);
|
|
1423
|
+
}, []);
|
|
1424
|
+
const retryStartup = useCallback(() => {
|
|
1425
|
+
setStartupError(null);
|
|
1426
|
+
setAuthRequired(false);
|
|
1427
|
+
setOnboardingLoading(true);
|
|
1428
|
+
setStartupPhase("starting-backend");
|
|
1429
|
+
setStartupRetryNonce((prev) => prev + 1);
|
|
1430
|
+
}, []);
|
|
1431
|
+
const handleReset = useCallback(async () => {
|
|
1432
|
+
if (lifecycleBusyRef.current) {
|
|
1433
|
+
const activeAction = lifecycleActionRef.current ?? lifecycleAction ?? "reset";
|
|
1434
|
+
setActionNotice(`Agent action already in progress (${LIFECYCLE_MESSAGES[activeAction].inProgress}). Please wait.`, "info", 2800);
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
const confirmed = await confirmDesktopAction({
|
|
1438
|
+
title: "Reset Agent",
|
|
1439
|
+
message: "This will completely reset the agent, wiping all config, memory, and data.",
|
|
1440
|
+
detail: "You will be taken back to the onboarding wizard.",
|
|
1441
|
+
confirmLabel: "Reset",
|
|
1442
|
+
cancelLabel: "Cancel",
|
|
1443
|
+
type: "warning",
|
|
1444
|
+
});
|
|
1445
|
+
if (!confirmed)
|
|
1446
|
+
return;
|
|
1447
|
+
if (!beginLifecycleAction("reset"))
|
|
1448
|
+
return;
|
|
1449
|
+
setActionNotice(LIFECYCLE_MESSAGES.reset.progress, "info", 3200);
|
|
1450
|
+
try {
|
|
1451
|
+
await client.resetAgent();
|
|
1452
|
+
setAgentStatus(null);
|
|
1453
|
+
setOnboardingComplete(false);
|
|
1454
|
+
setOnboardingStep("wakeUp");
|
|
1455
|
+
setConversationMessages([]);
|
|
1456
|
+
setActiveConversationId(null);
|
|
1457
|
+
activeConversationIdRef.current = null;
|
|
1458
|
+
setConversations([]);
|
|
1459
|
+
setPlugins([]);
|
|
1460
|
+
setSkills([]);
|
|
1461
|
+
setLogs([]);
|
|
1462
|
+
try {
|
|
1463
|
+
const options = await client.getOnboardingOptions();
|
|
1464
|
+
setOnboardingOptions(options);
|
|
1465
|
+
}
|
|
1466
|
+
catch {
|
|
1467
|
+
/* ignore */
|
|
1468
|
+
}
|
|
1469
|
+
setActionNotice(LIFECYCLE_MESSAGES.reset.success, "success", 3200);
|
|
1470
|
+
}
|
|
1471
|
+
catch (err) {
|
|
1472
|
+
setActionNotice(`Failed to ${LIFECYCLE_MESSAGES.reset.verb} agent: ${err instanceof Error ? err.message : "unknown error"}`, "error", 4200);
|
|
1473
|
+
await alertDesktopMessage({
|
|
1474
|
+
title: "Reset Failed",
|
|
1475
|
+
message: "Reset failed. Check the console for details.",
|
|
1476
|
+
type: "error",
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
finally {
|
|
1480
|
+
finishLifecycleAction();
|
|
1481
|
+
}
|
|
1482
|
+
}, [
|
|
1483
|
+
lifecycleAction,
|
|
1484
|
+
beginLifecycleAction,
|
|
1485
|
+
finishLifecycleAction,
|
|
1486
|
+
setActionNotice,
|
|
1487
|
+
]);
|
|
1488
|
+
const handleNewConversation = useCallback(async (title) => {
|
|
1489
|
+
conversationHydrationEpochRef.current += 1;
|
|
1490
|
+
const previousConversationId = activeConversationIdRef.current;
|
|
1491
|
+
const previousMessages = conversationMessagesRef.current;
|
|
1492
|
+
const previousCutoffTs = companionMessageCutoffTs;
|
|
1493
|
+
greetingFiredRef.current = false;
|
|
1494
|
+
greetingInFlightConversationRef.current = null;
|
|
1495
|
+
setChatInput("");
|
|
1496
|
+
setChatPendingImages([]);
|
|
1497
|
+
setChatSending(false);
|
|
1498
|
+
setChatFirstTokenReceived(false);
|
|
1499
|
+
setConversationMessages([]);
|
|
1500
|
+
setActiveConversationId(null);
|
|
1501
|
+
activeConversationIdRef.current = null;
|
|
1502
|
+
setCompanionMessageCutoffTs(Date.now());
|
|
1503
|
+
try {
|
|
1504
|
+
const { conversation, greeting } = await client.createConversation(title, {
|
|
1505
|
+
bootstrapGreeting: true,
|
|
1506
|
+
lang: uiLanguage,
|
|
1507
|
+
});
|
|
1508
|
+
const nextCutoffTs = Date.now();
|
|
1509
|
+
setConversations((prev) => [conversation, ...prev]);
|
|
1510
|
+
setActiveConversationId(conversation.id);
|
|
1511
|
+
activeConversationIdRef.current = conversation.id;
|
|
1512
|
+
setCompanionMessageCutoffTs(nextCutoffTs);
|
|
1513
|
+
const greetingText = greeting?.text?.trim() ?? "";
|
|
1514
|
+
if (greetingText) {
|
|
1515
|
+
greetingFiredRef.current = true;
|
|
1516
|
+
if (greeting?.persisted === true) {
|
|
1517
|
+
scheduleGreetingWaveForCompanion();
|
|
1518
|
+
}
|
|
1519
|
+
setConversationMessages([
|
|
1520
|
+
{
|
|
1521
|
+
id: `greeting-${Date.now()}`,
|
|
1522
|
+
role: "assistant",
|
|
1523
|
+
text: greetingText,
|
|
1524
|
+
timestamp: Date.now(),
|
|
1525
|
+
source: "agent_greeting",
|
|
1526
|
+
},
|
|
1527
|
+
]);
|
|
1528
|
+
}
|
|
1529
|
+
else {
|
|
1530
|
+
greetingFiredRef.current = false;
|
|
1531
|
+
setConversationMessages([]);
|
|
1532
|
+
void requestGreetingWhenRunning(conversation.id);
|
|
1533
|
+
}
|
|
1534
|
+
client.sendWsMessage({
|
|
1535
|
+
type: "active-conversation",
|
|
1536
|
+
conversationId: conversation.id,
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
catch {
|
|
1540
|
+
setActiveConversationId(previousConversationId);
|
|
1541
|
+
activeConversationIdRef.current = previousConversationId;
|
|
1542
|
+
setConversationMessages(previousMessages);
|
|
1543
|
+
setCompanionMessageCutoffTs(previousCutoffTs);
|
|
1544
|
+
greetingFiredRef.current = previousMessages.length > 0;
|
|
1545
|
+
if (previousConversationId) {
|
|
1546
|
+
client.sendWsMessage({
|
|
1547
|
+
type: "active-conversation",
|
|
1548
|
+
conversationId: previousConversationId,
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}, [
|
|
1553
|
+
companionMessageCutoffTs,
|
|
1554
|
+
requestGreetingWhenRunning,
|
|
1555
|
+
scheduleGreetingWaveForCompanion,
|
|
1556
|
+
uiLanguage,
|
|
1557
|
+
]);
|
|
1558
|
+
useEffect(() => {
|
|
1559
|
+
if (uiShellMode !== "companion" || tab !== "companion") {
|
|
1560
|
+
companionStaleConversationRefreshRef.current = null;
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
if (!activeConversationId) {
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
if (!shouldStartFreshCompanionConversation(conversationMessages)) {
|
|
1567
|
+
companionStaleConversationRefreshRef.current = null;
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1570
|
+
if (companionStaleConversationRefreshRef.current === activeConversationId) {
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
companionStaleConversationRefreshRef.current = activeConversationId;
|
|
1574
|
+
void handleNewConversation();
|
|
1575
|
+
}, [
|
|
1576
|
+
activeConversationId,
|
|
1577
|
+
conversationMessages,
|
|
1578
|
+
handleNewConversation,
|
|
1579
|
+
tab,
|
|
1580
|
+
uiShellMode,
|
|
1581
|
+
]);
|
|
1582
|
+
const appendLocalCommandTurn = useCallback((userText, assistantText) => {
|
|
1583
|
+
const now = Date.now();
|
|
1584
|
+
const nonce = Math.random().toString(36).slice(2, 8);
|
|
1585
|
+
setConversationMessages((prev) => [
|
|
1586
|
+
...prev,
|
|
1587
|
+
{
|
|
1588
|
+
id: `local-user-${now}-${nonce}`,
|
|
1589
|
+
role: "user",
|
|
1590
|
+
text: userText,
|
|
1591
|
+
timestamp: now,
|
|
1592
|
+
},
|
|
1593
|
+
{
|
|
1594
|
+
id: `local-assistant-${now}-${nonce}`,
|
|
1595
|
+
role: "assistant",
|
|
1596
|
+
text: assistantText,
|
|
1597
|
+
timestamp: now,
|
|
1598
|
+
source: "local_command",
|
|
1599
|
+
},
|
|
1600
|
+
]);
|
|
1601
|
+
}, []);
|
|
1602
|
+
const tryHandlePrefixedChatCommand = useCallback(async (rawText) => {
|
|
1603
|
+
const slash = parseSlashCommandInput(rawText);
|
|
1604
|
+
if (slash) {
|
|
1605
|
+
const savedCommand = loadSavedCustomCommands().find((command) => normalizeSlashCommandName(command.name) === slash.name);
|
|
1606
|
+
if (savedCommand) {
|
|
1607
|
+
const rewrittenText = expandSavedCustomCommand(savedCommand.text, slash.argsRaw);
|
|
1608
|
+
if (!rewrittenText.trim()) {
|
|
1609
|
+
appendLocalCommandTurn(rawText, `Saved command "/${slash.name}" is empty.`);
|
|
1610
|
+
return { handled: true };
|
|
1611
|
+
}
|
|
1612
|
+
return { handled: false, rewrittenText };
|
|
1613
|
+
}
|
|
1614
|
+
if (slash.name === "commands") {
|
|
1615
|
+
const customActions = (await client.listCustomActions()).filter((action) => action.enabled);
|
|
1616
|
+
const customCommandNames = customActions
|
|
1617
|
+
.map((action) => `/${action.name.toLowerCase()}`)
|
|
1618
|
+
.sort();
|
|
1619
|
+
const savedCommandNames = loadSavedCustomCommands()
|
|
1620
|
+
.map((command) => `/${normalizeSlashCommandName(command.name)}`)
|
|
1621
|
+
.sort();
|
|
1622
|
+
const lines = [
|
|
1623
|
+
formatSearchBullet("Saved / commands", savedCommandNames),
|
|
1624
|
+
formatSearchBullet("Custom action / commands", customCommandNames),
|
|
1625
|
+
"Use #remember ... to save memory notes. Use #memory or #knowledge to target retrieval.",
|
|
1626
|
+
"Use $query for a quick, non-persistent context answer.",
|
|
1627
|
+
];
|
|
1628
|
+
appendLocalCommandTurn(rawText, lines.join("\n\n"));
|
|
1629
|
+
return { handled: true };
|
|
1630
|
+
}
|
|
1631
|
+
let customActions = [];
|
|
1632
|
+
try {
|
|
1633
|
+
customActions = (await client.listCustomActions()).filter((action) => action.enabled);
|
|
1634
|
+
}
|
|
1635
|
+
catch {
|
|
1636
|
+
// If custom actions can't be loaded, fall back to normal slash routing.
|
|
1637
|
+
return { handled: false };
|
|
1638
|
+
}
|
|
1639
|
+
const customAction = customActions.find((action) => `/${normalizeCustomActionName(action.name).toLowerCase()}` ===
|
|
1640
|
+
slash.name);
|
|
1641
|
+
if (customAction) {
|
|
1642
|
+
const { params, missingRequired } = parseCustomActionParams(customAction, slash.argsRaw);
|
|
1643
|
+
if (missingRequired.length > 0) {
|
|
1644
|
+
appendLocalCommandTurn(rawText, `Missing required parameter(s): ${missingRequired.join(", ")}`);
|
|
1645
|
+
return { handled: true };
|
|
1646
|
+
}
|
|
1647
|
+
const result = await client.testCustomAction(customAction.id, params);
|
|
1648
|
+
if (!result.ok) {
|
|
1649
|
+
appendLocalCommandTurn(rawText, `Custom action "${customAction.name}" failed: ${result.error ?? "unknown error"}`);
|
|
1650
|
+
return { handled: true };
|
|
1651
|
+
}
|
|
1652
|
+
appendLocalCommandTurn(rawText, result.output?.trim() || `(no output from ${customAction.name})`);
|
|
1653
|
+
return { handled: true };
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
if (rawText.startsWith("#")) {
|
|
1657
|
+
const commandBody = rawText.slice(1).trim();
|
|
1658
|
+
if (!commandBody) {
|
|
1659
|
+
appendLocalCommandTurn(rawText, "Usage: #remember <text>, #memory <query>, #knowledge <query>, or #<query>.");
|
|
1660
|
+
return { handled: true };
|
|
1661
|
+
}
|
|
1662
|
+
const lower = commandBody.toLowerCase();
|
|
1663
|
+
if (lower.startsWith("remember ") ||
|
|
1664
|
+
lower.startsWith("remmeber ") ||
|
|
1665
|
+
lower.startsWith("save ")) {
|
|
1666
|
+
const memoryText = commandBody
|
|
1667
|
+
.replace(/^(remember|remmeber|save)\s+/i, "")
|
|
1668
|
+
.trim();
|
|
1669
|
+
if (!memoryText) {
|
|
1670
|
+
appendLocalCommandTurn(rawText, "Nothing to remember.");
|
|
1671
|
+
return { handled: true };
|
|
1672
|
+
}
|
|
1673
|
+
await client.rememberMemory(memoryText);
|
|
1674
|
+
appendLocalCommandTurn(rawText, `Saved memory note: "${memoryText}"`);
|
|
1675
|
+
return { handled: true };
|
|
1676
|
+
}
|
|
1677
|
+
let scope = "all";
|
|
1678
|
+
let query = commandBody;
|
|
1679
|
+
if (lower.startsWith("memory ")) {
|
|
1680
|
+
scope = "memory";
|
|
1681
|
+
query = commandBody.slice("memory ".length).trim();
|
|
1682
|
+
}
|
|
1683
|
+
else if (lower.startsWith("knowledge ")) {
|
|
1684
|
+
scope = "knowledge";
|
|
1685
|
+
query = commandBody.slice("knowledge ".length).trim();
|
|
1686
|
+
}
|
|
1687
|
+
else if (lower.startsWith("all ")) {
|
|
1688
|
+
scope = "all";
|
|
1689
|
+
query = commandBody.slice("all ".length).trim();
|
|
1690
|
+
}
|
|
1691
|
+
if (!query) {
|
|
1692
|
+
appendLocalCommandTurn(rawText, "Search query cannot be empty.");
|
|
1693
|
+
return { handled: true };
|
|
1694
|
+
}
|
|
1695
|
+
const [memoryResult, knowledgeResult] = await Promise.all([
|
|
1696
|
+
scope === "knowledge"
|
|
1697
|
+
? Promise.resolve(null)
|
|
1698
|
+
: client.searchMemory(query, { limit: 6 }),
|
|
1699
|
+
scope === "memory"
|
|
1700
|
+
? Promise.resolve(null)
|
|
1701
|
+
: client.searchKnowledge(query, { threshold: 0.2, limit: 6 }),
|
|
1702
|
+
]);
|
|
1703
|
+
const memoryLines = memoryResult?.results.map((item, index) => `${index + 1}. ${item.text.replace(/\s+/g, " ").trim()}`) ?? [];
|
|
1704
|
+
const knowledgeLines = knowledgeResult?.results.map((item, index) => `${index + 1}. ${item.text.replace(/\s+/g, " ").trim()} (sim ${item.similarity.toFixed(2)})`) ?? [];
|
|
1705
|
+
appendLocalCommandTurn(rawText, [
|
|
1706
|
+
scope === "memory"
|
|
1707
|
+
? "Memory search"
|
|
1708
|
+
: scope === "knowledge"
|
|
1709
|
+
? "Knowledge search"
|
|
1710
|
+
: "Memory + knowledge search",
|
|
1711
|
+
"",
|
|
1712
|
+
scope === "knowledge"
|
|
1713
|
+
? ""
|
|
1714
|
+
: formatSearchBullet("Memories", memoryLines),
|
|
1715
|
+
scope === "memory"
|
|
1716
|
+
? ""
|
|
1717
|
+
: formatSearchBullet("Knowledge", knowledgeLines),
|
|
1718
|
+
]
|
|
1719
|
+
.filter(Boolean)
|
|
1720
|
+
.join("\n\n"));
|
|
1721
|
+
return { handled: true };
|
|
1722
|
+
}
|
|
1723
|
+
if (rawText.startsWith("$")) {
|
|
1724
|
+
const queryRaw = rawText.slice(1).trim();
|
|
1725
|
+
if (queryRaw) {
|
|
1726
|
+
appendLocalCommandTurn(rawText, "Use bare `$` only. `$ <text>` is not supported.");
|
|
1727
|
+
return { handled: true };
|
|
1728
|
+
}
|
|
1729
|
+
const query = "What is most relevant from memory and knowledge right now?";
|
|
1730
|
+
const quick = await client.quickContext(query, { limit: 6 });
|
|
1731
|
+
const memoryLines = quick.memories.map((item, index) => `${index + 1}. ${item.text.replace(/\s+/g, " ").trim()}`);
|
|
1732
|
+
const knowledgeLines = quick.knowledge.map((item, index) => `${index + 1}. ${item.text.replace(/\s+/g, " ").trim()} (sim ${item.similarity.toFixed(2)})`);
|
|
1733
|
+
appendLocalCommandTurn(rawText, [
|
|
1734
|
+
quick.answer,
|
|
1735
|
+
"",
|
|
1736
|
+
formatSearchBullet("Memories used", memoryLines),
|
|
1737
|
+
formatSearchBullet("Knowledge used", knowledgeLines),
|
|
1738
|
+
].join("\n"));
|
|
1739
|
+
return { handled: true };
|
|
1740
|
+
}
|
|
1741
|
+
return { handled: false };
|
|
1742
|
+
}, [appendLocalCommandTurn]);
|
|
1743
|
+
const sendChatText = useCallback(async (rawInput, options) => {
|
|
1744
|
+
const rawText = rawInput.trim();
|
|
1745
|
+
if (!rawText)
|
|
1746
|
+
return;
|
|
1747
|
+
if (chatSendBusyRef.current)
|
|
1748
|
+
return;
|
|
1749
|
+
chatSendBusyRef.current = true;
|
|
1750
|
+
const sendNonce = ++chatSendNonceRef.current;
|
|
1751
|
+
const channelType = options?.channelType ?? "DM";
|
|
1752
|
+
const conversationMode = channelType === "VOICE_DM" || channelType === "VOICE_GROUP"
|
|
1753
|
+
? "simple"
|
|
1754
|
+
: chatMode;
|
|
1755
|
+
const imagesToSend = options?.images;
|
|
1756
|
+
let controller = null;
|
|
1757
|
+
try {
|
|
1758
|
+
let text = rawText;
|
|
1759
|
+
let commandResult;
|
|
1760
|
+
try {
|
|
1761
|
+
commandResult = await tryHandlePrefixedChatCommand(rawText);
|
|
1762
|
+
}
|
|
1763
|
+
catch (err) {
|
|
1764
|
+
appendLocalCommandTurn(rawText, `Command failed: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
1765
|
+
if (options?.clearChatInput) {
|
|
1766
|
+
setChatInput("");
|
|
1767
|
+
}
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
if (commandResult.handled) {
|
|
1771
|
+
if (options?.clearChatInput) {
|
|
1772
|
+
setChatInput("");
|
|
1773
|
+
}
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
if (typeof commandResult.rewrittenText === "string" &&
|
|
1777
|
+
commandResult.rewrittenText.trim()) {
|
|
1778
|
+
text = commandResult.rewrittenText.trim();
|
|
1779
|
+
}
|
|
1780
|
+
let convId = options?.conversationId ?? activeConversationId ?? "";
|
|
1781
|
+
if (!convId) {
|
|
1782
|
+
try {
|
|
1783
|
+
const { conversation } = await client.createConversation();
|
|
1784
|
+
const nextCutoffTs = Date.now();
|
|
1785
|
+
setConversations((prev) => [conversation, ...prev]);
|
|
1786
|
+
setActiveConversationId(conversation.id);
|
|
1787
|
+
activeConversationIdRef.current = conversation.id;
|
|
1788
|
+
setCompanionMessageCutoffTs(nextCutoffTs);
|
|
1789
|
+
convId = conversation.id;
|
|
1790
|
+
}
|
|
1791
|
+
catch {
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
client.sendWsMessage({
|
|
1796
|
+
type: "active-conversation",
|
|
1797
|
+
conversationId: convId,
|
|
1798
|
+
});
|
|
1799
|
+
const now = Date.now();
|
|
1800
|
+
const userMsgId = `temp-${now}`;
|
|
1801
|
+
const assistantMsgId = `temp-resp-${now}`;
|
|
1802
|
+
setCompanionMessageCutoffTs(now);
|
|
1803
|
+
setConversationMessages((prev) => [
|
|
1804
|
+
...prev,
|
|
1805
|
+
{ id: userMsgId, role: "user", text, timestamp: now },
|
|
1806
|
+
{ id: assistantMsgId, role: "assistant", text: "", timestamp: now },
|
|
1807
|
+
]);
|
|
1808
|
+
if (options?.clearChatInput) {
|
|
1809
|
+
setChatInput("");
|
|
1810
|
+
}
|
|
1811
|
+
setChatSending(true);
|
|
1812
|
+
setChatFirstTokenReceived(false);
|
|
1813
|
+
controller = new AbortController();
|
|
1814
|
+
chatAbortRef.current = controller;
|
|
1815
|
+
let streamedAssistantText = "";
|
|
1816
|
+
try {
|
|
1817
|
+
const data = await client.sendConversationMessageStream(convId, text, (token, accumulatedText) => {
|
|
1818
|
+
const nextText = typeof accumulatedText === "string"
|
|
1819
|
+
? accumulatedText
|
|
1820
|
+
: mergeStreamingText(streamedAssistantText, token);
|
|
1821
|
+
if (nextText === streamedAssistantText)
|
|
1822
|
+
return;
|
|
1823
|
+
streamedAssistantText = nextText;
|
|
1824
|
+
setChatFirstTokenReceived(true);
|
|
1825
|
+
setConversationMessages((prev) => prev.map((message) => message.id !== assistantMsgId
|
|
1826
|
+
? message
|
|
1827
|
+
: message.text === nextText
|
|
1828
|
+
? message
|
|
1829
|
+
: { ...message, text: nextText }));
|
|
1830
|
+
}, channelType, controller.signal, imagesToSend, conversationMode);
|
|
1831
|
+
if (!data.text.trim()) {
|
|
1832
|
+
setConversationMessages((prev) => prev.filter((message) => message.id !== assistantMsgId));
|
|
1833
|
+
}
|
|
1834
|
+
else if (shouldApplyFinalStreamText(streamedAssistantText, data.text)) {
|
|
1835
|
+
setConversationMessages((prev) => {
|
|
1836
|
+
let changed = false;
|
|
1837
|
+
const next = prev.map((message) => {
|
|
1838
|
+
if (message.id !== assistantMsgId)
|
|
1839
|
+
return message;
|
|
1840
|
+
if (message.text === data.text)
|
|
1841
|
+
return message;
|
|
1842
|
+
changed = true;
|
|
1843
|
+
return { ...message, text: data.text };
|
|
1844
|
+
});
|
|
1845
|
+
return changed ? next : prev;
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
if (data.usage) {
|
|
1849
|
+
setChatLastUsage({
|
|
1850
|
+
promptTokens: data.usage.promptTokens,
|
|
1851
|
+
completionTokens: data.usage.completionTokens,
|
|
1852
|
+
totalTokens: data.usage.totalTokens,
|
|
1853
|
+
model: data.usage.model,
|
|
1854
|
+
updatedAt: Date.now(),
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
if (!data.completed && streamedAssistantText.trim()) {
|
|
1858
|
+
setConversationMessages((prev) => prev.map((message) => message.id === assistantMsgId
|
|
1859
|
+
? { ...message, interrupted: true }
|
|
1860
|
+
: message));
|
|
1861
|
+
}
|
|
1862
|
+
void loadConversations();
|
|
1863
|
+
}
|
|
1864
|
+
catch (err) {
|
|
1865
|
+
const abortError = err;
|
|
1866
|
+
if (abortError.name === "AbortError") {
|
|
1867
|
+
setConversationMessages((prev) => prev.filter((message) => !(message.id === assistantMsgId && !message.text.trim())));
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
const status = err.status;
|
|
1871
|
+
if (status === 404) {
|
|
1872
|
+
try {
|
|
1873
|
+
const { conversation } = await client.createConversation();
|
|
1874
|
+
const nextCutoffTs = Date.now();
|
|
1875
|
+
setConversations((prev) => [conversation, ...prev]);
|
|
1876
|
+
setActiveConversationId(conversation.id);
|
|
1877
|
+
activeConversationIdRef.current = conversation.id;
|
|
1878
|
+
setCompanionMessageCutoffTs(nextCutoffTs);
|
|
1879
|
+
client.sendWsMessage({
|
|
1880
|
+
type: "active-conversation",
|
|
1881
|
+
conversationId: conversation.id,
|
|
1882
|
+
});
|
|
1883
|
+
const retryData = await client.sendConversationMessage(conversation.id, text, channelType, imagesToSend, conversationMode);
|
|
1884
|
+
setConversationMessages(filterRenderableConversationMessages([
|
|
1885
|
+
{
|
|
1886
|
+
id: `temp-${Date.now()}`,
|
|
1887
|
+
role: "user",
|
|
1888
|
+
text,
|
|
1889
|
+
timestamp: Date.now(),
|
|
1890
|
+
},
|
|
1891
|
+
{
|
|
1892
|
+
id: `temp-resp-${Date.now()}`,
|
|
1893
|
+
role: "assistant",
|
|
1894
|
+
text: retryData.text,
|
|
1895
|
+
timestamp: Date.now(),
|
|
1896
|
+
},
|
|
1897
|
+
]));
|
|
1898
|
+
}
|
|
1899
|
+
catch {
|
|
1900
|
+
setConversationMessages((prev) => prev.filter((message) => !(message.id === assistantMsgId && !message.text.trim())));
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
else {
|
|
1904
|
+
await loadConversationMessages(convId);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
finally {
|
|
1908
|
+
if (chatAbortRef.current === controller) {
|
|
1909
|
+
chatAbortRef.current = null;
|
|
1910
|
+
}
|
|
1911
|
+
if (chatSendNonceRef.current === sendNonce) {
|
|
1912
|
+
chatSendBusyRef.current = false;
|
|
1913
|
+
setChatSending(false);
|
|
1914
|
+
setChatFirstTokenReceived(false);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
finally {
|
|
1919
|
+
if (controller == null && chatSendNonceRef.current === sendNonce) {
|
|
1920
|
+
chatSendBusyRef.current = false;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}, [
|
|
1924
|
+
activeConversationId,
|
|
1925
|
+
appendLocalCommandTurn,
|
|
1926
|
+
chatMode,
|
|
1927
|
+
loadConversationMessages,
|
|
1928
|
+
loadConversations,
|
|
1929
|
+
tryHandlePrefixedChatCommand,
|
|
1930
|
+
]);
|
|
1931
|
+
const handleChatSend = useCallback(async (channelType = "DM") => {
|
|
1932
|
+
const imagesToSend = chatPendingImages.length
|
|
1933
|
+
? chatPendingImages
|
|
1934
|
+
: undefined;
|
|
1935
|
+
setChatPendingImages([]);
|
|
1936
|
+
await sendChatText(chatInput, {
|
|
1937
|
+
channelType,
|
|
1938
|
+
images: imagesToSend,
|
|
1939
|
+
clearChatInput: true,
|
|
1940
|
+
});
|
|
1941
|
+
}, [chatInput, chatPendingImages, sendChatText]);
|
|
1942
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: t is stable but defined later
|
|
1943
|
+
const sendActionMessage = useCallback(async (text) => {
|
|
1944
|
+
const trimmed = text.trim();
|
|
1945
|
+
if (!trimmed)
|
|
1946
|
+
return;
|
|
1947
|
+
if (chatSendBusyRef.current)
|
|
1948
|
+
return;
|
|
1949
|
+
chatSendBusyRef.current = true;
|
|
1950
|
+
const sendNonce = ++chatSendNonceRef.current;
|
|
1951
|
+
const conversationMode = chatMode;
|
|
1952
|
+
let controller = null;
|
|
1953
|
+
try {
|
|
1954
|
+
let convId = activeConversationId ?? "";
|
|
1955
|
+
if (!convId) {
|
|
1956
|
+
try {
|
|
1957
|
+
const { conversation } = await client.createConversation(t("conversations.newChatTitle"));
|
|
1958
|
+
const nextCutoffTs = Date.now();
|
|
1959
|
+
setConversations((prev) => [conversation, ...prev]);
|
|
1960
|
+
setActiveConversationId(conversation.id);
|
|
1961
|
+
activeConversationIdRef.current = conversation.id;
|
|
1962
|
+
setCompanionMessageCutoffTs(nextCutoffTs);
|
|
1963
|
+
convId = conversation.id;
|
|
1964
|
+
}
|
|
1965
|
+
catch {
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
client.sendWsMessage({
|
|
1970
|
+
type: "active-conversation",
|
|
1971
|
+
conversationId: convId,
|
|
1972
|
+
});
|
|
1973
|
+
const now = Date.now();
|
|
1974
|
+
const userMsgId = `temp-action-${now}`;
|
|
1975
|
+
const assistantMsgId = `temp-action-resp-${now}`;
|
|
1976
|
+
setCompanionMessageCutoffTs(now);
|
|
1977
|
+
setConversationMessages((prev) => [
|
|
1978
|
+
...prev,
|
|
1979
|
+
{ id: userMsgId, role: "user", text: trimmed, timestamp: now },
|
|
1980
|
+
{ id: assistantMsgId, role: "assistant", text: "", timestamp: now },
|
|
1981
|
+
]);
|
|
1982
|
+
setChatSending(true);
|
|
1983
|
+
setChatFirstTokenReceived(false);
|
|
1984
|
+
controller = new AbortController();
|
|
1985
|
+
chatAbortRef.current = controller;
|
|
1986
|
+
let streamedAssistantText = "";
|
|
1987
|
+
try {
|
|
1988
|
+
const data = await client.sendConversationMessageStream(convId, trimmed, (token, accumulatedText) => {
|
|
1989
|
+
const nextText = typeof accumulatedText === "string"
|
|
1990
|
+
? accumulatedText
|
|
1991
|
+
: mergeStreamingText(streamedAssistantText, token);
|
|
1992
|
+
if (nextText === streamedAssistantText)
|
|
1993
|
+
return;
|
|
1994
|
+
streamedAssistantText = nextText;
|
|
1995
|
+
setChatFirstTokenReceived(true);
|
|
1996
|
+
setConversationMessages((prev) => prev.map((message) => message.id !== assistantMsgId
|
|
1997
|
+
? message
|
|
1998
|
+
: message.text === nextText
|
|
1999
|
+
? message
|
|
2000
|
+
: { ...message, text: nextText }));
|
|
2001
|
+
}, "DM", controller.signal, undefined, conversationMode);
|
|
2002
|
+
if (!data.text.trim()) {
|
|
2003
|
+
setConversationMessages((prev) => prev.filter((message) => message.id !== assistantMsgId));
|
|
2004
|
+
}
|
|
2005
|
+
else if (shouldApplyFinalStreamText(streamedAssistantText, data.text)) {
|
|
2006
|
+
setConversationMessages((prev) => {
|
|
2007
|
+
let changed = false;
|
|
2008
|
+
const next = prev.map((message) => {
|
|
2009
|
+
if (message.id !== assistantMsgId)
|
|
2010
|
+
return message;
|
|
2011
|
+
if (message.text === data.text)
|
|
2012
|
+
return message;
|
|
2013
|
+
changed = true;
|
|
2014
|
+
return { ...message, text: data.text };
|
|
2015
|
+
});
|
|
2016
|
+
return changed ? next : prev;
|
|
2017
|
+
});
|
|
2018
|
+
}
|
|
2019
|
+
void loadConversations();
|
|
2020
|
+
}
|
|
2021
|
+
catch (err) {
|
|
2022
|
+
const abortError = err;
|
|
2023
|
+
if (abortError.name === "AbortError") {
|
|
2024
|
+
setConversationMessages((prev) => prev.filter((message) => !(message.id === assistantMsgId && !message.text.trim())));
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
await loadConversationMessages(convId);
|
|
2028
|
+
}
|
|
2029
|
+
finally {
|
|
2030
|
+
if (chatAbortRef.current === controller) {
|
|
2031
|
+
chatAbortRef.current = null;
|
|
2032
|
+
}
|
|
2033
|
+
if (chatSendNonceRef.current === sendNonce) {
|
|
2034
|
+
chatSendBusyRef.current = false;
|
|
2035
|
+
setChatSending(false);
|
|
2036
|
+
setChatFirstTokenReceived(false);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
finally {
|
|
2041
|
+
if (controller == null && chatSendNonceRef.current === sendNonce) {
|
|
2042
|
+
chatSendBusyRef.current = false;
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
}, [
|
|
2046
|
+
chatMode,
|
|
2047
|
+
activeConversationId,
|
|
2048
|
+
loadConversationMessages,
|
|
2049
|
+
loadConversations,
|
|
2050
|
+
]);
|
|
2051
|
+
const handleChatStop = useCallback(() => {
|
|
2052
|
+
chatSendBusyRef.current = false;
|
|
2053
|
+
chatAbortRef.current?.abort();
|
|
2054
|
+
chatAbortRef.current = null;
|
|
2055
|
+
setChatSending(false);
|
|
2056
|
+
setChatFirstTokenReceived(false);
|
|
2057
|
+
// Also stop any active PTY sessions — the user wants everything to halt
|
|
2058
|
+
for (const session of ptySessions) {
|
|
2059
|
+
client.stopCodingAgent(session.sessionId).catch(() => { });
|
|
2060
|
+
}
|
|
2061
|
+
}, [ptySessions]);
|
|
2062
|
+
const handleChatRetry = useCallback((assistantMsgId) => {
|
|
2063
|
+
let retryText = null;
|
|
2064
|
+
setConversationMessages((prev) => {
|
|
2065
|
+
// Find the interrupted assistant message
|
|
2066
|
+
const assistantIdx = prev.findIndex((m) => m.id === assistantMsgId && m.role === "assistant");
|
|
2067
|
+
if (assistantIdx < 0)
|
|
2068
|
+
return prev;
|
|
2069
|
+
// Find the preceding user message
|
|
2070
|
+
let userMsg = null;
|
|
2071
|
+
for (let i = assistantIdx - 1; i >= 0; i--) {
|
|
2072
|
+
if (prev[i].role === "user") {
|
|
2073
|
+
userMsg = prev[i];
|
|
2074
|
+
break;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
if (!userMsg)
|
|
2078
|
+
return prev;
|
|
2079
|
+
// Remove the interrupted assistant message
|
|
2080
|
+
const next = prev.filter((m) => m.id !== assistantMsgId);
|
|
2081
|
+
retryText = userMsg.text;
|
|
2082
|
+
return next;
|
|
2083
|
+
});
|
|
2084
|
+
if (retryText) {
|
|
2085
|
+
void sendChatText(retryText);
|
|
2086
|
+
}
|
|
2087
|
+
}, [sendChatText]);
|
|
2088
|
+
const handleChatEdit = useCallback(async (messageId, text) => {
|
|
2089
|
+
const convId = activeConversationIdRef.current;
|
|
2090
|
+
const nextText = text.trim();
|
|
2091
|
+
if (!convId || !nextText) {
|
|
2092
|
+
return false;
|
|
2093
|
+
}
|
|
2094
|
+
let currentMessages = conversationMessagesRef.current;
|
|
2095
|
+
let messageIndex = currentMessages.findIndex((message) => message.id === messageId && message.role === "user");
|
|
2096
|
+
if (messageIndex < 0) {
|
|
2097
|
+
const loaded = await loadConversationMessages(convId);
|
|
2098
|
+
if (!loaded.ok) {
|
|
2099
|
+
return false;
|
|
2100
|
+
}
|
|
2101
|
+
currentMessages = conversationMessagesRef.current;
|
|
2102
|
+
messageIndex = currentMessages.findIndex((message) => message.id === messageId && message.role === "user");
|
|
2103
|
+
}
|
|
2104
|
+
if (messageIndex < 0) {
|
|
2105
|
+
return false;
|
|
2106
|
+
}
|
|
2107
|
+
const targetMessage = currentMessages[messageIndex];
|
|
2108
|
+
if (targetMessage.source === "local_command" ||
|
|
2109
|
+
targetMessage.id.startsWith("temp-")) {
|
|
2110
|
+
return false;
|
|
2111
|
+
}
|
|
2112
|
+
chatSendBusyRef.current = false;
|
|
2113
|
+
chatAbortRef.current?.abort();
|
|
2114
|
+
chatAbortRef.current = null;
|
|
2115
|
+
setChatSending(false);
|
|
2116
|
+
setChatFirstTokenReceived(false);
|
|
2117
|
+
setChatInput("");
|
|
2118
|
+
const preservedMessages = currentMessages.slice(0, messageIndex);
|
|
2119
|
+
conversationMessagesRef.current = preservedMessages;
|
|
2120
|
+
setConversationMessages(preservedMessages);
|
|
2121
|
+
try {
|
|
2122
|
+
await client.truncateConversationMessages(convId, messageId, {
|
|
2123
|
+
inclusive: true,
|
|
2124
|
+
});
|
|
2125
|
+
await sendChatText(nextText, { conversationId: convId });
|
|
2126
|
+
return true;
|
|
2127
|
+
}
|
|
2128
|
+
catch (err) {
|
|
2129
|
+
await loadConversationMessages(convId);
|
|
2130
|
+
setActionNotice(`Failed to edit message: ${err instanceof Error ? err.message : "network error"}`, "error", 4200);
|
|
2131
|
+
return false;
|
|
2132
|
+
}
|
|
2133
|
+
}, [loadConversationMessages, sendChatText, setActionNotice]);
|
|
2134
|
+
const handleChatClear = useCallback(async () => {
|
|
2135
|
+
const convId = activeConversationId;
|
|
2136
|
+
if (!convId) {
|
|
2137
|
+
setActionNotice("No active conversation to clear.", "info", 2200);
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
try {
|
|
2141
|
+
await client.deleteConversation(convId);
|
|
2142
|
+
setActiveConversationId(null);
|
|
2143
|
+
activeConversationIdRef.current = null;
|
|
2144
|
+
setConversationMessages([]);
|
|
2145
|
+
setUnreadConversations((prev) => {
|
|
2146
|
+
const next = new Set(prev);
|
|
2147
|
+
next.delete(convId);
|
|
2148
|
+
return next;
|
|
2149
|
+
});
|
|
2150
|
+
await loadConversations();
|
|
2151
|
+
}
|
|
2152
|
+
catch (err) {
|
|
2153
|
+
const status = err.status;
|
|
2154
|
+
if (status === 404) {
|
|
2155
|
+
setActiveConversationId(null);
|
|
2156
|
+
activeConversationIdRef.current = null;
|
|
2157
|
+
setConversationMessages([]);
|
|
2158
|
+
setUnreadConversations((prev) => {
|
|
2159
|
+
const next = new Set(prev);
|
|
2160
|
+
next.delete(convId);
|
|
2161
|
+
return next;
|
|
2162
|
+
});
|
|
2163
|
+
await loadConversations();
|
|
2164
|
+
setActionNotice("Conversation was already cleared.", "info", 2600);
|
|
2165
|
+
return;
|
|
2166
|
+
}
|
|
2167
|
+
setActionNotice(`Failed to clear conversation: ${err instanceof Error ? err.message : "network error"}`, "error", 4200);
|
|
2168
|
+
}
|
|
2169
|
+
}, [activeConversationId, loadConversations, setActionNotice]);
|
|
2170
|
+
const handleSelectConversation = useCallback(async (id) => {
|
|
2171
|
+
conversationHydrationEpochRef.current += 1;
|
|
2172
|
+
if (id === activeConversationId &&
|
|
2173
|
+
conversationMessagesRef.current.length > 0)
|
|
2174
|
+
return;
|
|
2175
|
+
const previousActive = activeConversationId;
|
|
2176
|
+
setActiveConversationId(id);
|
|
2177
|
+
activeConversationIdRef.current = id;
|
|
2178
|
+
client.sendWsMessage({ type: "active-conversation", conversationId: id });
|
|
2179
|
+
setUnreadConversations((prev) => {
|
|
2180
|
+
const next = new Set(prev);
|
|
2181
|
+
next.delete(id);
|
|
2182
|
+
return next;
|
|
2183
|
+
});
|
|
2184
|
+
const loaded = await loadConversationMessages(id);
|
|
2185
|
+
if (loaded.ok)
|
|
2186
|
+
return;
|
|
2187
|
+
if (loaded.status === 404) {
|
|
2188
|
+
const refreshed = await loadConversations();
|
|
2189
|
+
const fallbackId = refreshed?.[0]?.id ?? null;
|
|
2190
|
+
if (fallbackId) {
|
|
2191
|
+
setActiveConversationId(fallbackId);
|
|
2192
|
+
activeConversationIdRef.current = fallbackId;
|
|
2193
|
+
client.sendWsMessage({
|
|
2194
|
+
type: "active-conversation",
|
|
2195
|
+
conversationId: fallbackId,
|
|
2196
|
+
});
|
|
2197
|
+
const fallbackLoaded = await loadConversationMessages(fallbackId);
|
|
2198
|
+
if (!fallbackLoaded.ok) {
|
|
2199
|
+
setActionNotice(`Failed to load fallback conversation: ${fallbackLoaded.message}`, "error", 4200);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
else {
|
|
2203
|
+
setActiveConversationId(null);
|
|
2204
|
+
activeConversationIdRef.current = null;
|
|
2205
|
+
setConversationMessages([]);
|
|
2206
|
+
}
|
|
2207
|
+
setActionNotice("Conversation was not found. Refreshed the conversation list.", "info", 3200);
|
|
2208
|
+
return;
|
|
2209
|
+
}
|
|
2210
|
+
setActiveConversationId(previousActive);
|
|
2211
|
+
activeConversationIdRef.current = previousActive;
|
|
2212
|
+
if (previousActive) {
|
|
2213
|
+
client.sendWsMessage({
|
|
2214
|
+
type: "active-conversation",
|
|
2215
|
+
conversationId: previousActive,
|
|
2216
|
+
});
|
|
2217
|
+
const restored = await loadConversationMessages(previousActive);
|
|
2218
|
+
if (!restored.ok) {
|
|
2219
|
+
setActionNotice(`Failed to restore previous conversation: ${restored.message}`, "error", 4200);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
else {
|
|
2223
|
+
setConversationMessages([]);
|
|
2224
|
+
}
|
|
2225
|
+
setActionNotice(`Failed to load conversation: ${loaded.message}`, "error", 4200);
|
|
2226
|
+
}, [
|
|
2227
|
+
activeConversationId,
|
|
2228
|
+
loadConversationMessages,
|
|
2229
|
+
loadConversations,
|
|
2230
|
+
setActionNotice,
|
|
2231
|
+
]);
|
|
2232
|
+
const handleDeleteConversation = useCallback(async (id) => {
|
|
2233
|
+
const deletingActive = activeConversationId === id;
|
|
2234
|
+
try {
|
|
2235
|
+
await client.deleteConversation(id);
|
|
2236
|
+
setConversations((prev) => prev.filter((conversation) => conversation.id !== id));
|
|
2237
|
+
setUnreadConversations((prev) => {
|
|
2238
|
+
const next = new Set(prev);
|
|
2239
|
+
next.delete(id);
|
|
2240
|
+
return next;
|
|
2241
|
+
});
|
|
2242
|
+
if (deletingActive) {
|
|
2243
|
+
setActiveConversationId(null);
|
|
2244
|
+
activeConversationIdRef.current = null;
|
|
2245
|
+
setConversationMessages([]);
|
|
2246
|
+
}
|
|
2247
|
+
const refreshed = await loadConversations();
|
|
2248
|
+
if (deletingActive) {
|
|
2249
|
+
const fallbackId = refreshed?.[0]?.id ?? null;
|
|
2250
|
+
if (fallbackId) {
|
|
2251
|
+
setActiveConversationId(fallbackId);
|
|
2252
|
+
activeConversationIdRef.current = fallbackId;
|
|
2253
|
+
client.sendWsMessage({
|
|
2254
|
+
type: "active-conversation",
|
|
2255
|
+
conversationId: fallbackId,
|
|
2256
|
+
});
|
|
2257
|
+
const fallbackLoaded = await loadConversationMessages(fallbackId);
|
|
2258
|
+
if (!fallbackLoaded.ok) {
|
|
2259
|
+
setActionNotice(`Failed to load fallback conversation: ${fallbackLoaded.message}`, "error", 4200);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
catch (err) {
|
|
2265
|
+
const status = err.status;
|
|
2266
|
+
if (status === 404) {
|
|
2267
|
+
setConversations((prev) => prev.filter((conversation) => conversation.id !== id));
|
|
2268
|
+
setUnreadConversations((prev) => {
|
|
2269
|
+
const next = new Set(prev);
|
|
2270
|
+
next.delete(id);
|
|
2271
|
+
return next;
|
|
2272
|
+
});
|
|
2273
|
+
if (deletingActive) {
|
|
2274
|
+
setActiveConversationId(null);
|
|
2275
|
+
activeConversationIdRef.current = null;
|
|
2276
|
+
setConversationMessages([]);
|
|
2277
|
+
}
|
|
2278
|
+
await loadConversations();
|
|
2279
|
+
setActionNotice("Conversation was already deleted. Refreshed the conversation list.", "info", 3200);
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
setActionNotice(`Failed to delete conversation: ${err instanceof Error ? err.message : "network error"}`, "error", 4200);
|
|
2283
|
+
}
|
|
2284
|
+
}, [
|
|
2285
|
+
activeConversationId,
|
|
2286
|
+
loadConversationMessages,
|
|
2287
|
+
loadConversations,
|
|
2288
|
+
setActionNotice,
|
|
2289
|
+
]);
|
|
2290
|
+
const handleRenameConversation = useCallback(async (id, title) => {
|
|
2291
|
+
const trimmed = title.trim();
|
|
2292
|
+
if (!trimmed) {
|
|
2293
|
+
setActionNotice("Conversation title cannot be empty.", "error", 2800);
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
2296
|
+
try {
|
|
2297
|
+
const { conversation } = await client.renameConversation(id, trimmed);
|
|
2298
|
+
setConversations((prev) => prev.map((existing) => existing.id === id ? conversation : existing));
|
|
2299
|
+
}
|
|
2300
|
+
catch (err) {
|
|
2301
|
+
const status = err.status;
|
|
2302
|
+
if (status === 404) {
|
|
2303
|
+
await loadConversations();
|
|
2304
|
+
setActionNotice("Conversation was not found. Refreshed the conversation list.", "info", 3200);
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
setActionNotice(`Failed to rename conversation: ${err instanceof Error ? err.message : "network error"}`, "error", 4200);
|
|
2308
|
+
}
|
|
2309
|
+
}, [loadConversations, setActionNotice]);
|
|
2310
|
+
// ── Pairing ────────────────────────────────────────────────────────
|
|
2311
|
+
const handlePairingSubmit = useCallback(async () => {
|
|
2312
|
+
if (pairingBusyRef.current || pairingBusy)
|
|
2313
|
+
return;
|
|
2314
|
+
const code = pairingCodeInput.trim();
|
|
2315
|
+
if (!code) {
|
|
2316
|
+
setPairingError("Enter the pairing code from the server logs.");
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
setPairingError(null);
|
|
2320
|
+
pairingBusyRef.current = true;
|
|
2321
|
+
setPairingBusy(true);
|
|
2322
|
+
try {
|
|
2323
|
+
const { token } = await client.pair(code);
|
|
2324
|
+
client.setToken(token);
|
|
2325
|
+
window.location.reload();
|
|
2326
|
+
}
|
|
2327
|
+
catch (err) {
|
|
2328
|
+
const status = err.status;
|
|
2329
|
+
if (status === 410)
|
|
2330
|
+
setPairingError("Pairing code expired. Check logs for a new code.");
|
|
2331
|
+
else if (status === 429)
|
|
2332
|
+
setPairingError("Too many attempts. Try again later.");
|
|
2333
|
+
else
|
|
2334
|
+
setPairingError("Pairing failed. Check the code and try again.");
|
|
2335
|
+
}
|
|
2336
|
+
finally {
|
|
2337
|
+
pairingBusyRef.current = false;
|
|
2338
|
+
setPairingBusy(false);
|
|
2339
|
+
}
|
|
2340
|
+
}, [pairingBusy, pairingCodeInput]);
|
|
2341
|
+
// ── Plugin actions ─────────────────────────────────────────────────
|
|
2342
|
+
const handlePluginToggle = useCallback(async (pluginId, enabled) => {
|
|
2343
|
+
const plugin = plugins.find((p) => p.id === pluginId);
|
|
2344
|
+
const pluginName = plugin?.name ?? pluginId;
|
|
2345
|
+
if (enabled &&
|
|
2346
|
+
plugin?.validationErrors &&
|
|
2347
|
+
plugin.validationErrors.length > 0) {
|
|
2348
|
+
setPluginSettingsOpen((prev) => new Set([...prev, pluginId]));
|
|
2349
|
+
setActionNotice(`${pluginName} has required settings. Configure them after enabling.`, "info", 3400);
|
|
2350
|
+
}
|
|
2351
|
+
try {
|
|
2352
|
+
setActionNotice(`${enabled ? "Enabling" : "Disabling"} ${pluginName}...`, "info", 4200);
|
|
2353
|
+
await client.updatePlugin(pluginId, { enabled });
|
|
2354
|
+
await loadPlugins();
|
|
2355
|
+
setActionNotice(`${pluginName} ${enabled ? "enabled" : "disabled"}. Restart required to apply.`, "success", 2800);
|
|
2356
|
+
}
|
|
2357
|
+
catch (err) {
|
|
2358
|
+
await loadPlugins().catch(() => {
|
|
2359
|
+
/* ignore */
|
|
2360
|
+
});
|
|
2361
|
+
setActionNotice(`Failed to ${enabled ? "enable" : "disable"} ${pluginName}: ${err instanceof Error ? err.message : "unknown error"}`, "error", 4200);
|
|
2362
|
+
}
|
|
2363
|
+
}, [plugins, loadPlugins, setActionNotice]);
|
|
2364
|
+
const handlePluginConfigSave = useCallback(async (pluginId, config) => {
|
|
2365
|
+
if (Object.keys(config).length === 0)
|
|
2366
|
+
return;
|
|
2367
|
+
setPluginSaving((prev) => new Set([...prev, pluginId]));
|
|
2368
|
+
try {
|
|
2369
|
+
await client.updatePlugin(pluginId, { config });
|
|
2370
|
+
// Check if this is an AI provider plugin
|
|
2371
|
+
const plugin = plugins.find((p) => p.id === pluginId);
|
|
2372
|
+
const isAiProvider = plugin?.category === "ai-provider";
|
|
2373
|
+
await loadPlugins();
|
|
2374
|
+
setActionNotice(isAiProvider
|
|
2375
|
+
? "Provider settings saved. Restart required to apply."
|
|
2376
|
+
: "Plugin settings saved.", "success");
|
|
2377
|
+
setPluginSaveSuccess((prev) => new Set([...prev, pluginId]));
|
|
2378
|
+
setTimeout(() => {
|
|
2379
|
+
setPluginSaveSuccess((prev) => {
|
|
2380
|
+
const next = new Set(prev);
|
|
2381
|
+
next.delete(pluginId);
|
|
2382
|
+
return next;
|
|
2383
|
+
});
|
|
2384
|
+
}, 2000);
|
|
2385
|
+
}
|
|
2386
|
+
catch (err) {
|
|
2387
|
+
setActionNotice(`Save failed: ${err instanceof Error ? err.message : "unknown error"}`, "error", 3800);
|
|
2388
|
+
}
|
|
2389
|
+
finally {
|
|
2390
|
+
setPluginSaving((prev) => {
|
|
2391
|
+
const next = new Set(prev);
|
|
2392
|
+
next.delete(pluginId);
|
|
2393
|
+
return next;
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
}, [loadPlugins, setActionNotice, plugins]);
|
|
2397
|
+
// ── Skill actions ──────────────────────────────────────────────────
|
|
2398
|
+
const handleSkillToggle = useCallback(async (skillId, enabled) => {
|
|
2399
|
+
setSkillToggleAction(skillId);
|
|
2400
|
+
try {
|
|
2401
|
+
const { skill } = await client.updateSkill(skillId, enabled);
|
|
2402
|
+
setSkills((prev) => prev.map((s) => s.id === skillId ? { ...s, enabled: skill.enabled } : s));
|
|
2403
|
+
setActionNotice(`${skill.name} ${skill.enabled ? "enabled" : "disabled"}.`, "success");
|
|
2404
|
+
}
|
|
2405
|
+
catch (err) {
|
|
2406
|
+
setActionNotice(`Failed to update skill: ${err instanceof Error ? err.message : "unknown error"}`, "error", 4200);
|
|
2407
|
+
}
|
|
2408
|
+
finally {
|
|
2409
|
+
setSkillToggleAction("");
|
|
2410
|
+
}
|
|
2411
|
+
}, [setActionNotice]);
|
|
2412
|
+
const handleCreateSkill = useCallback(async () => {
|
|
2413
|
+
const name = skillCreateName.trim();
|
|
2414
|
+
if (!name)
|
|
2415
|
+
return;
|
|
2416
|
+
setSkillCreating(true);
|
|
2417
|
+
try {
|
|
2418
|
+
const result = await client.createSkill(name, skillCreateDescription.trim() || "");
|
|
2419
|
+
setSkillCreateName("");
|
|
2420
|
+
setSkillCreateDescription("");
|
|
2421
|
+
setSkillCreateFormOpen(false);
|
|
2422
|
+
setActionNotice(`Skill "${name}" created.`, "success");
|
|
2423
|
+
await refreshSkills();
|
|
2424
|
+
if (result.path)
|
|
2425
|
+
await client.openSkill(result.skill?.id ?? name).catch(() => undefined);
|
|
2426
|
+
}
|
|
2427
|
+
catch (err) {
|
|
2428
|
+
setActionNotice(`Failed to create skill: ${err instanceof Error ? err.message : "error"}`, "error", 4200);
|
|
2429
|
+
}
|
|
2430
|
+
finally {
|
|
2431
|
+
setSkillCreating(false);
|
|
2432
|
+
}
|
|
2433
|
+
}, [skillCreateName, skillCreateDescription, refreshSkills, setActionNotice]);
|
|
2434
|
+
const handleOpenSkill = useCallback(async (skillId) => {
|
|
2435
|
+
try {
|
|
2436
|
+
await client.openSkill(skillId);
|
|
2437
|
+
setActionNotice("Opening skill folder...", "success", 2000);
|
|
2438
|
+
}
|
|
2439
|
+
catch (err) {
|
|
2440
|
+
setActionNotice(`Failed to open: ${err instanceof Error ? err.message : "error"}`, "error", 4200);
|
|
2441
|
+
}
|
|
2442
|
+
}, [setActionNotice]);
|
|
2443
|
+
const handleDeleteSkill = useCallback(async (skillId, skillName) => {
|
|
2444
|
+
const confirmed = await confirmDesktopAction({
|
|
2445
|
+
title: "Delete Skill",
|
|
2446
|
+
message: `Delete skill "${skillName}"?`,
|
|
2447
|
+
detail: "This cannot be undone.",
|
|
2448
|
+
confirmLabel: "Delete",
|
|
2449
|
+
cancelLabel: "Cancel",
|
|
2450
|
+
type: "warning",
|
|
2451
|
+
});
|
|
2452
|
+
if (!confirmed)
|
|
2453
|
+
return;
|
|
2454
|
+
try {
|
|
2455
|
+
await client.deleteSkill(skillId);
|
|
2456
|
+
setActionNotice(`Skill "${skillName}" deleted.`, "success");
|
|
2457
|
+
await refreshSkills();
|
|
2458
|
+
}
|
|
2459
|
+
catch (err) {
|
|
2460
|
+
setActionNotice(`Failed to delete: ${err instanceof Error ? err.message : "error"}`, "error", 4200);
|
|
2461
|
+
}
|
|
2462
|
+
}, [refreshSkills, setActionNotice]);
|
|
2463
|
+
const handleReviewSkill = useCallback(async (skillId) => {
|
|
2464
|
+
setSkillReviewId(skillId);
|
|
2465
|
+
setSkillReviewLoading(true);
|
|
2466
|
+
setSkillReviewReport(null);
|
|
2467
|
+
try {
|
|
2468
|
+
const { report } = await client.getSkillScanReport(skillId);
|
|
2469
|
+
setSkillReviewReport(report);
|
|
2470
|
+
}
|
|
2471
|
+
catch {
|
|
2472
|
+
setSkillReviewReport(null);
|
|
2473
|
+
}
|
|
2474
|
+
finally {
|
|
2475
|
+
setSkillReviewLoading(false);
|
|
2476
|
+
}
|
|
2477
|
+
}, []);
|
|
2478
|
+
const handleAcknowledgeSkill = useCallback(async (skillId) => {
|
|
2479
|
+
try {
|
|
2480
|
+
await client.acknowledgeSkill(skillId, true);
|
|
2481
|
+
setActionNotice(`Skill "${skillId}" acknowledged and enabled.`, "success");
|
|
2482
|
+
setSkillReviewReport(null);
|
|
2483
|
+
setSkillReviewId("");
|
|
2484
|
+
await refreshSkills();
|
|
2485
|
+
}
|
|
2486
|
+
catch (err) {
|
|
2487
|
+
setActionNotice(`Failed: ${err instanceof Error ? err.message : "error"}`, "error", 4200);
|
|
2488
|
+
}
|
|
2489
|
+
}, [refreshSkills, setActionNotice]);
|
|
2490
|
+
const searchSkillsMarketplace = useCallback(async () => {
|
|
2491
|
+
const query = skillsMarketplaceQuery.trim();
|
|
2492
|
+
if (!query) {
|
|
2493
|
+
setSkillsMarketplaceResults([]);
|
|
2494
|
+
setSkillsMarketplaceError("");
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2497
|
+
setSkillsMarketplaceLoading(true);
|
|
2498
|
+
setSkillsMarketplaceError("");
|
|
2499
|
+
try {
|
|
2500
|
+
const { results } = await client.searchSkillsMarketplace(query, false, 20);
|
|
2501
|
+
setSkillsMarketplaceResults(results);
|
|
2502
|
+
}
|
|
2503
|
+
catch (err) {
|
|
2504
|
+
setSkillsMarketplaceResults([]);
|
|
2505
|
+
setSkillsMarketplaceError(err instanceof Error ? err.message : "unknown error");
|
|
2506
|
+
}
|
|
2507
|
+
finally {
|
|
2508
|
+
setSkillsMarketplaceLoading(false);
|
|
2509
|
+
}
|
|
2510
|
+
}, [skillsMarketplaceQuery]);
|
|
2511
|
+
const installSkillFromMarketplace = useCallback(async (item) => {
|
|
2512
|
+
setSkillsMarketplaceAction(`install:${item.id}`);
|
|
2513
|
+
try {
|
|
2514
|
+
await client.installMarketplaceSkill({
|
|
2515
|
+
slug: item.slug ?? item.id,
|
|
2516
|
+
githubUrl: item.githubUrl,
|
|
2517
|
+
repository: item.repository,
|
|
2518
|
+
path: item.path ?? undefined,
|
|
2519
|
+
name: item.name,
|
|
2520
|
+
description: item.description,
|
|
2521
|
+
source: item.source ?? "clawhub",
|
|
2522
|
+
autoRefresh: true,
|
|
2523
|
+
});
|
|
2524
|
+
await refreshSkills();
|
|
2525
|
+
setActionNotice(`Installed skill: ${item.name}`, "success");
|
|
2526
|
+
}
|
|
2527
|
+
catch (err) {
|
|
2528
|
+
setActionNotice(`Skill install failed: ${err instanceof Error ? err.message : "unknown error"}`, "error", 4200);
|
|
2529
|
+
}
|
|
2530
|
+
finally {
|
|
2531
|
+
setSkillsMarketplaceAction("");
|
|
2532
|
+
}
|
|
2533
|
+
}, [refreshSkills, setActionNotice]);
|
|
2534
|
+
const installSkillFromGithubUrl = useCallback(async () => {
|
|
2535
|
+
const githubUrl = skillsMarketplaceManualGithubUrl.trim();
|
|
2536
|
+
if (!githubUrl)
|
|
2537
|
+
return;
|
|
2538
|
+
setSkillsMarketplaceAction("install:manual");
|
|
2539
|
+
try {
|
|
2540
|
+
let repository;
|
|
2541
|
+
let skillPath;
|
|
2542
|
+
let inferredName;
|
|
2543
|
+
try {
|
|
2544
|
+
const parsed = new URL(githubUrl);
|
|
2545
|
+
if (parsed.hostname === "github.com") {
|
|
2546
|
+
const parts = parsed.pathname.split("/").filter(Boolean);
|
|
2547
|
+
if (parts.length >= 2)
|
|
2548
|
+
repository = `${parts[0]}/${parts[1]}`;
|
|
2549
|
+
if (parts[2] === "tree" && parts.length >= 5) {
|
|
2550
|
+
skillPath = parts.slice(4).join("/");
|
|
2551
|
+
inferredName = parts[parts.length - 1];
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
catch {
|
|
2556
|
+
/* keep raw URL */
|
|
2557
|
+
}
|
|
2558
|
+
await client.installMarketplaceSkill({
|
|
2559
|
+
githubUrl,
|
|
2560
|
+
repository,
|
|
2561
|
+
path: skillPath,
|
|
2562
|
+
name: inferredName,
|
|
2563
|
+
source: "manual",
|
|
2564
|
+
autoRefresh: true,
|
|
2565
|
+
});
|
|
2566
|
+
setSkillsMarketplaceManualGithubUrl("");
|
|
2567
|
+
await refreshSkills();
|
|
2568
|
+
setActionNotice("Skill installed from GitHub URL.", "success");
|
|
2569
|
+
}
|
|
2570
|
+
catch (err) {
|
|
2571
|
+
setActionNotice(`GitHub install failed: ${err instanceof Error ? err.message : "unknown error"}`, "error", 4200);
|
|
2572
|
+
}
|
|
2573
|
+
finally {
|
|
2574
|
+
setSkillsMarketplaceAction("");
|
|
2575
|
+
}
|
|
2576
|
+
}, [skillsMarketplaceManualGithubUrl, refreshSkills, setActionNotice]);
|
|
2577
|
+
const uninstallMarketplaceSkill = useCallback(async (skillId, name) => {
|
|
2578
|
+
setSkillsMarketplaceAction(`uninstall:${skillId}`);
|
|
2579
|
+
try {
|
|
2580
|
+
await client.deleteSkill(skillId);
|
|
2581
|
+
await refreshSkills();
|
|
2582
|
+
setActionNotice(`Uninstalled skill: ${name}`, "success");
|
|
2583
|
+
}
|
|
2584
|
+
catch (err) {
|
|
2585
|
+
setActionNotice(`Skill uninstall failed: ${err instanceof Error ? err.message : "unknown error"}`, "error", 4200);
|
|
2586
|
+
}
|
|
2587
|
+
finally {
|
|
2588
|
+
setSkillsMarketplaceAction("");
|
|
2589
|
+
}
|
|
2590
|
+
}, [refreshSkills, setActionNotice]);
|
|
2591
|
+
// ── Inventory actions ──────────────────────────────────────────────
|
|
2592
|
+
const handleWalletApiKeySave = useCallback(async (config) => {
|
|
2593
|
+
if (Object.keys(config).length === 0)
|
|
2594
|
+
return;
|
|
2595
|
+
if (walletApiKeySavingRef.current || walletApiKeySaving)
|
|
2596
|
+
return;
|
|
2597
|
+
walletApiKeySavingRef.current = true;
|
|
2598
|
+
setWalletApiKeySaving(true);
|
|
2599
|
+
setWalletError(null);
|
|
2600
|
+
try {
|
|
2601
|
+
await client.updateWalletConfig(config);
|
|
2602
|
+
await loadWalletConfig();
|
|
2603
|
+
await loadBalances();
|
|
2604
|
+
setActionNotice("Wallet API keys saved. Restart required to apply.", "success");
|
|
2605
|
+
}
|
|
2606
|
+
catch (err) {
|
|
2607
|
+
setWalletError(`Failed to save API keys: ${err instanceof Error ? err.message : "network error"}`);
|
|
2608
|
+
}
|
|
2609
|
+
finally {
|
|
2610
|
+
walletApiKeySavingRef.current = false;
|
|
2611
|
+
setWalletApiKeySaving(false);
|
|
2612
|
+
}
|
|
2613
|
+
}, [walletApiKeySaving, loadWalletConfig, loadBalances, setActionNotice]);
|
|
2614
|
+
const handleExportKeys = useCallback(async () => {
|
|
2615
|
+
if (walletExportVisible) {
|
|
2616
|
+
setWalletExportVisible(false);
|
|
2617
|
+
setWalletExportData(null);
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
const confirmed = await confirmDesktopAction({
|
|
2621
|
+
title: "Reveal Private Keys",
|
|
2622
|
+
message: "This will reveal your private keys.",
|
|
2623
|
+
detail: "NEVER share your private keys with anyone. Anyone with your private keys can steal all funds in your wallets.",
|
|
2624
|
+
confirmLabel: "Continue",
|
|
2625
|
+
cancelLabel: "Cancel",
|
|
2626
|
+
type: "warning",
|
|
2627
|
+
});
|
|
2628
|
+
if (!confirmed)
|
|
2629
|
+
return;
|
|
2630
|
+
const exportToken = await promptModal({
|
|
2631
|
+
title: "Wallet Export Token",
|
|
2632
|
+
message: "Enter your wallet export token (MILADY_WALLET_EXPORT_TOKEN):",
|
|
2633
|
+
placeholder: "MILADY_WALLET_EXPORT_TOKEN",
|
|
2634
|
+
confirmLabel: "Export",
|
|
2635
|
+
cancelLabel: "Cancel",
|
|
2636
|
+
});
|
|
2637
|
+
if (exportToken === null)
|
|
2638
|
+
return;
|
|
2639
|
+
if (!exportToken.trim()) {
|
|
2640
|
+
setWalletError("Wallet export token is required.");
|
|
2641
|
+
return;
|
|
2642
|
+
}
|
|
2643
|
+
try {
|
|
2644
|
+
const data = await client.exportWalletKeys(exportToken.trim());
|
|
2645
|
+
setWalletExportData(data);
|
|
2646
|
+
setWalletExportVisible(true);
|
|
2647
|
+
setTimeout(() => {
|
|
2648
|
+
setWalletExportVisible(false);
|
|
2649
|
+
setWalletExportData(null);
|
|
2650
|
+
}, 60_000);
|
|
2651
|
+
}
|
|
2652
|
+
catch (err) {
|
|
2653
|
+
setWalletError(`Failed to export keys: ${err instanceof Error ? err.message : "network error"}`);
|
|
2654
|
+
}
|
|
2655
|
+
}, [promptModal, walletExportVisible]);
|
|
2656
|
+
// ── Registry / Drop / Whitelist actions ─────────────────────────────
|
|
2657
|
+
const loadRegistryStatus = useCallback(async () => {
|
|
2658
|
+
setRegistryLoading(true);
|
|
2659
|
+
setRegistryError(null);
|
|
2660
|
+
try {
|
|
2661
|
+
const status = await client.getRegistryStatus();
|
|
2662
|
+
setRegistryStatus(status);
|
|
2663
|
+
}
|
|
2664
|
+
catch (err) {
|
|
2665
|
+
setRegistryError(err instanceof Error ? err.message : "Failed to load registry status");
|
|
2666
|
+
}
|
|
2667
|
+
finally {
|
|
2668
|
+
setRegistryLoading(false);
|
|
2669
|
+
}
|
|
2670
|
+
}, []);
|
|
2671
|
+
const registerOnChain = useCallback(async () => {
|
|
2672
|
+
setRegistryRegistering(true);
|
|
2673
|
+
setRegistryError(null);
|
|
2674
|
+
try {
|
|
2675
|
+
await client.registerAgent({
|
|
2676
|
+
name: characterDraft?.name || agentStatus?.agentName,
|
|
2677
|
+
});
|
|
2678
|
+
await loadRegistryStatus();
|
|
2679
|
+
}
|
|
2680
|
+
catch (err) {
|
|
2681
|
+
setRegistryError(err instanceof Error ? err.message : "Registration failed");
|
|
2682
|
+
}
|
|
2683
|
+
finally {
|
|
2684
|
+
setRegistryRegistering(false);
|
|
2685
|
+
}
|
|
2686
|
+
}, [characterDraft?.name, agentStatus?.agentName, loadRegistryStatus]);
|
|
2687
|
+
const syncRegistryProfile = useCallback(async () => {
|
|
2688
|
+
setRegistryRegistering(true);
|
|
2689
|
+
setRegistryError(null);
|
|
2690
|
+
try {
|
|
2691
|
+
await client.syncRegistryProfile({
|
|
2692
|
+
name: characterDraft?.name || agentStatus?.agentName,
|
|
2693
|
+
});
|
|
2694
|
+
await loadRegistryStatus();
|
|
2695
|
+
}
|
|
2696
|
+
catch (err) {
|
|
2697
|
+
setRegistryError(err instanceof Error ? err.message : "Sync failed");
|
|
2698
|
+
}
|
|
2699
|
+
finally {
|
|
2700
|
+
setRegistryRegistering(false);
|
|
2701
|
+
}
|
|
2702
|
+
}, [characterDraft?.name, agentStatus?.agentName, loadRegistryStatus]);
|
|
2703
|
+
const loadDropStatus = useCallback(async () => {
|
|
2704
|
+
setDropLoading(true);
|
|
2705
|
+
try {
|
|
2706
|
+
const status = await client.getDropStatus();
|
|
2707
|
+
setDropStatus(status);
|
|
2708
|
+
}
|
|
2709
|
+
catch {
|
|
2710
|
+
// Non-critical -- drop may not be configured
|
|
2711
|
+
}
|
|
2712
|
+
finally {
|
|
2713
|
+
setDropLoading(false);
|
|
2714
|
+
}
|
|
2715
|
+
}, []);
|
|
2716
|
+
const mintFromDrop = useCallback(async (shiny) => {
|
|
2717
|
+
setMintInProgress(true);
|
|
2718
|
+
setMintShiny(shiny);
|
|
2719
|
+
setMintError(null);
|
|
2720
|
+
setMintResult(null);
|
|
2721
|
+
try {
|
|
2722
|
+
const result = await client.mintAgent({
|
|
2723
|
+
name: characterDraft?.name || agentStatus?.agentName,
|
|
2724
|
+
shiny,
|
|
2725
|
+
});
|
|
2726
|
+
setMintResult(result);
|
|
2727
|
+
await loadRegistryStatus();
|
|
2728
|
+
await loadDropStatus();
|
|
2729
|
+
}
|
|
2730
|
+
catch (err) {
|
|
2731
|
+
setMintError(err instanceof Error ? err.message : "Mint failed");
|
|
2732
|
+
}
|
|
2733
|
+
finally {
|
|
2734
|
+
setMintInProgress(false);
|
|
2735
|
+
setMintShiny(false);
|
|
2736
|
+
}
|
|
2737
|
+
}, [
|
|
2738
|
+
characterDraft?.name,
|
|
2739
|
+
agentStatus?.agentName,
|
|
2740
|
+
loadRegistryStatus,
|
|
2741
|
+
loadDropStatus,
|
|
2742
|
+
]);
|
|
2743
|
+
const loadWhitelistStatus = useCallback(async () => {
|
|
2744
|
+
setWhitelistLoading(true);
|
|
2745
|
+
try {
|
|
2746
|
+
const status = await client.getWhitelistStatus();
|
|
2747
|
+
setWhitelistStatus(status);
|
|
2748
|
+
}
|
|
2749
|
+
catch {
|
|
2750
|
+
// Non-critical
|
|
2751
|
+
}
|
|
2752
|
+
finally {
|
|
2753
|
+
setWhitelistLoading(false);
|
|
2754
|
+
}
|
|
2755
|
+
}, []);
|
|
2756
|
+
// ── Character actions ──────────────────────────────────────────────
|
|
2757
|
+
const handleSaveCharacter = useCallback(async () => {
|
|
2758
|
+
setCharacterSaving(true);
|
|
2759
|
+
setCharacterSaveError(null);
|
|
2760
|
+
setCharacterSaveSuccess(null);
|
|
2761
|
+
try {
|
|
2762
|
+
const draft = prepareDraftForSave(characterDraft);
|
|
2763
|
+
const { agentName } = await client.updateCharacter(draft);
|
|
2764
|
+
// Also persist avatar selection to config (under "ui" which is allowlisted)
|
|
2765
|
+
try {
|
|
2766
|
+
await client.updateConfig({
|
|
2767
|
+
ui: { avatarIndex: selectedVrmIndex },
|
|
2768
|
+
});
|
|
2769
|
+
}
|
|
2770
|
+
catch {
|
|
2771
|
+
/* non-fatal */
|
|
2772
|
+
}
|
|
2773
|
+
setCharacterSaveSuccess("Character saved successfully.");
|
|
2774
|
+
if (agentName && agentStatus) {
|
|
2775
|
+
setAgentStatus({ ...agentStatus, agentName });
|
|
2776
|
+
}
|
|
2777
|
+
await loadCharacter();
|
|
2778
|
+
}
|
|
2779
|
+
catch (err) {
|
|
2780
|
+
setCharacterSaveError(`Failed to save: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
2781
|
+
}
|
|
2782
|
+
setCharacterSaving(false);
|
|
2783
|
+
}, [characterDraft, agentStatus, loadCharacter, selectedVrmIndex]);
|
|
2784
|
+
const handleCharacterFieldInput = useCallback((field, value) => {
|
|
2785
|
+
setCharacterDraft((prev) => ({ ...prev, [field]: value }));
|
|
2786
|
+
}, []);
|
|
2787
|
+
const handleCharacterArrayInput = useCallback((field, value) => {
|
|
2788
|
+
const items = value
|
|
2789
|
+
.split("\n")
|
|
2790
|
+
.map((s) => s.trim())
|
|
2791
|
+
.filter((s) => s.length > 0);
|
|
2792
|
+
setCharacterDraft((prev) => ({ ...prev, [field]: items }));
|
|
2793
|
+
}, []);
|
|
2794
|
+
const handleCharacterStyleInput = useCallback((subfield, value) => {
|
|
2795
|
+
const items = value
|
|
2796
|
+
.split("\n")
|
|
2797
|
+
.map((s) => s.trim())
|
|
2798
|
+
.filter((s) => s.length > 0);
|
|
2799
|
+
setCharacterDraft((prev) => ({
|
|
2800
|
+
...prev,
|
|
2801
|
+
style: { ...(prev.style ?? {}), [subfield]: items },
|
|
2802
|
+
}));
|
|
2803
|
+
}, []);
|
|
2804
|
+
const handleCharacterMessageExamplesInput = useCallback((value) => {
|
|
2805
|
+
if (!value.trim()) {
|
|
2806
|
+
setCharacterDraft((prev) => ({
|
|
2807
|
+
...prev,
|
|
2808
|
+
messageExamples: [],
|
|
2809
|
+
}));
|
|
2810
|
+
return;
|
|
2811
|
+
}
|
|
2812
|
+
const blocks = value.split(/\n\s*\n/).filter((b) => b.trim().length > 0);
|
|
2813
|
+
const parsed = blocks.map((block) => {
|
|
2814
|
+
const lines = block.split("\n").filter((l) => l.trim().length > 0);
|
|
2815
|
+
const examples = lines.map((line) => {
|
|
2816
|
+
const colonIdx = line.indexOf(":");
|
|
2817
|
+
if (colonIdx > 0) {
|
|
2818
|
+
return {
|
|
2819
|
+
name: line.slice(0, colonIdx).trim(),
|
|
2820
|
+
content: { text: line.slice(colonIdx + 1).trim() },
|
|
2821
|
+
};
|
|
2822
|
+
}
|
|
2823
|
+
return { name: "User", content: { text: line.trim() } };
|
|
2824
|
+
});
|
|
2825
|
+
return { examples };
|
|
2826
|
+
});
|
|
2827
|
+
setCharacterDraft((prev) => ({
|
|
2828
|
+
...prev,
|
|
2829
|
+
messageExamples: parsed,
|
|
2830
|
+
}));
|
|
2831
|
+
}, []);
|
|
2832
|
+
// ── Onboarding ─────────────────────────────────────────────────────
|
|
2833
|
+
const handleOnboardingFinish = useCallback(async () => {
|
|
2834
|
+
if (onboardingFinishBusyRef.current || onboardingRestarting)
|
|
2835
|
+
return;
|
|
2836
|
+
if (!onboardingOptions)
|
|
2837
|
+
return;
|
|
2838
|
+
if (onboardingFinishSavingRef.current || onboardingRestarting)
|
|
2839
|
+
return;
|
|
2840
|
+
const style = onboardingOptions.styles.find((s) => s.catchphrase === onboardingStyle);
|
|
2841
|
+
const systemPrompt = style?.system
|
|
2842
|
+
? style.system.replace(/\{\{name\}\}/g, onboardingName)
|
|
2843
|
+
: `You are ${onboardingName}, an autonomous AI agent powered by elizaOS. ${onboardingOptions.sharedStyleRules}`;
|
|
2844
|
+
const elizaCloudProvisioned = onboardingRunMode === "cloud" &&
|
|
2845
|
+
onboardingCloudProvider === "elizacloud" &&
|
|
2846
|
+
!onboardingRemoteConnected;
|
|
2847
|
+
const apiRunMode = elizaCloudProvisioned ? "cloud" : "local";
|
|
2848
|
+
onboardingFinishBusyRef.current = true;
|
|
2849
|
+
setOnboardingRestarting(true);
|
|
2850
|
+
onboardingFinishSavingRef.current = true;
|
|
2851
|
+
try {
|
|
2852
|
+
// Build inventoryProviders from RPC selections/keys
|
|
2853
|
+
const inventoryProviders = [];
|
|
2854
|
+
const rpcSel = onboardingRpcSelections;
|
|
2855
|
+
const rpcK = onboardingRpcKeys;
|
|
2856
|
+
const defaultRpcProvider = elizaCloudProvisioned || elizaCloudConnected
|
|
2857
|
+
? "eliza-cloud"
|
|
2858
|
+
: undefined;
|
|
2859
|
+
const evmProvider = rpcSel.evm || defaultRpcProvider;
|
|
2860
|
+
const bscProvider = rpcSel.bsc || defaultRpcProvider;
|
|
2861
|
+
const solanaProvider = rpcSel.solana || defaultRpcProvider;
|
|
2862
|
+
if (evmProvider) {
|
|
2863
|
+
inventoryProviders.push({
|
|
2864
|
+
chain: "evm",
|
|
2865
|
+
rpcProvider: evmProvider,
|
|
2866
|
+
rpcApiKey: rpcK.ALCHEMY_API_KEY || undefined,
|
|
2867
|
+
});
|
|
2868
|
+
}
|
|
2869
|
+
if (bscProvider) {
|
|
2870
|
+
inventoryProviders.push({
|
|
2871
|
+
chain: "bsc",
|
|
2872
|
+
rpcProvider: bscProvider,
|
|
2873
|
+
rpcApiKey: rpcK.ALCHEMY_API_KEY || undefined,
|
|
2874
|
+
});
|
|
2875
|
+
}
|
|
2876
|
+
if (solanaProvider) {
|
|
2877
|
+
inventoryProviders.push({
|
|
2878
|
+
chain: "solana",
|
|
2879
|
+
rpcProvider: solanaProvider,
|
|
2880
|
+
rpcApiKey: rpcK.HELIUS_API_KEY || undefined,
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
await client.submitOnboarding({
|
|
2884
|
+
name: onboardingName,
|
|
2885
|
+
runMode: apiRunMode,
|
|
2886
|
+
sandboxMode: "off",
|
|
2887
|
+
bio: style?.bio ?? ["An autonomous AI agent."],
|
|
2888
|
+
systemPrompt,
|
|
2889
|
+
style: style?.style,
|
|
2890
|
+
adjectives: style?.adjectives,
|
|
2891
|
+
postExamples: style?.postExamples,
|
|
2892
|
+
messageExamples: style?.messageExamples,
|
|
2893
|
+
cloudProvider: elizaCloudProvisioned
|
|
2894
|
+
? onboardingCloudProvider
|
|
2895
|
+
: undefined,
|
|
2896
|
+
smallModel: elizaCloudProvisioned
|
|
2897
|
+
? onboardingSmallModel.trim() || undefined
|
|
2898
|
+
: undefined,
|
|
2899
|
+
largeModel: elizaCloudProvisioned
|
|
2900
|
+
? onboardingLargeModel.trim() || undefined
|
|
2901
|
+
: undefined,
|
|
2902
|
+
provider: apiRunMode === "local" ? onboardingProvider || undefined : undefined,
|
|
2903
|
+
providerApiKey: onboardingApiKey || undefined,
|
|
2904
|
+
primaryModel: apiRunMode === "local"
|
|
2905
|
+
? onboardingPrimaryModel.trim() || undefined
|
|
2906
|
+
: undefined,
|
|
2907
|
+
inventoryProviders: inventoryProviders.length > 0 ? inventoryProviders : undefined,
|
|
2908
|
+
});
|
|
2909
|
+
try {
|
|
2910
|
+
setAgentStatus(await client.restartAgent());
|
|
2911
|
+
}
|
|
2912
|
+
catch {
|
|
2913
|
+
/* ignore */
|
|
2914
|
+
}
|
|
2915
|
+
const greetConvId = await hydrateInitialConversationState();
|
|
2916
|
+
await requestGreetingWhenRunning(greetConvId, { showOverlay: true });
|
|
2917
|
+
setOnboardingComplete(true);
|
|
2918
|
+
setTab("chat");
|
|
2919
|
+
}
|
|
2920
|
+
catch (err) {
|
|
2921
|
+
await alertDesktopMessage({
|
|
2922
|
+
title: "Setup Failed",
|
|
2923
|
+
message: `${err instanceof Error ? err.message : "network error"}. Please try again.`,
|
|
2924
|
+
type: "error",
|
|
2925
|
+
});
|
|
2926
|
+
}
|
|
2927
|
+
finally {
|
|
2928
|
+
onboardingFinishSavingRef.current = false;
|
|
2929
|
+
onboardingFinishBusyRef.current = false;
|
|
2930
|
+
setOnboardingRestarting(false);
|
|
2931
|
+
}
|
|
2932
|
+
}, [
|
|
2933
|
+
onboardingRestarting,
|
|
2934
|
+
onboardingOptions,
|
|
2935
|
+
onboardingStyle,
|
|
2936
|
+
onboardingName,
|
|
2937
|
+
onboardingRunMode,
|
|
2938
|
+
onboardingCloudProvider,
|
|
2939
|
+
onboardingSmallModel,
|
|
2940
|
+
onboardingLargeModel,
|
|
2941
|
+
onboardingProvider,
|
|
2942
|
+
onboardingApiKey,
|
|
2943
|
+
onboardingRemoteConnected,
|
|
2944
|
+
onboardingPrimaryModel,
|
|
2945
|
+
onboardingRpcSelections,
|
|
2946
|
+
onboardingRpcKeys,
|
|
2947
|
+
hydrateInitialConversationState,
|
|
2948
|
+
setTab,
|
|
2949
|
+
elizaCloudConnected,
|
|
2950
|
+
requestGreetingWhenRunning,
|
|
2951
|
+
]);
|
|
2952
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: t is stable but defined later
|
|
2953
|
+
const handleOnboardingNext = useCallback(async (options) => {
|
|
2954
|
+
const STEP_ORDER = [
|
|
2955
|
+
"wakeUp",
|
|
2956
|
+
"connection",
|
|
2957
|
+
"rpc",
|
|
2958
|
+
"senses",
|
|
2959
|
+
"activate",
|
|
2960
|
+
];
|
|
2961
|
+
// Hardcode agent name to Eliza
|
|
2962
|
+
if (onboardingStep === "wakeUp" && !onboardingName) {
|
|
2963
|
+
setState("onboardingName", "Eliza");
|
|
2964
|
+
}
|
|
2965
|
+
// Auto-select first style if none chosen
|
|
2966
|
+
if (onboardingStep === "wakeUp" &&
|
|
2967
|
+
!onboardingStyle &&
|
|
2968
|
+
onboardingOptions?.styles?.length) {
|
|
2969
|
+
setState("onboardingStyle", onboardingOptions.styles[0].catchphrase);
|
|
2970
|
+
}
|
|
2971
|
+
// At activate step, finish onboarding
|
|
2972
|
+
if (onboardingStep === "activate") {
|
|
2973
|
+
await handleOnboardingFinish();
|
|
2974
|
+
return;
|
|
2975
|
+
}
|
|
2976
|
+
// At senses step, check permissions unless bypass
|
|
2977
|
+
if (onboardingStep === "senses") {
|
|
2978
|
+
if (options?.allowPermissionBypass) {
|
|
2979
|
+
await handleOnboardingFinish();
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2982
|
+
try {
|
|
2983
|
+
const permissions = await client.getPermissions();
|
|
2984
|
+
const missingPermissions = getMissingOnboardingPermissions(permissions);
|
|
2985
|
+
if (missingPermissions.length > 0) {
|
|
2986
|
+
const missingLabels = missingPermissions
|
|
2987
|
+
.map((id) => ONBOARDING_PERMISSION_LABELS[id] ?? id)
|
|
2988
|
+
.join(", ");
|
|
2989
|
+
setActionNotice(`Missing required permissions: ${missingLabels}. Grant them or use "Skip for Now".`, "error", 5200);
|
|
2990
|
+
return;
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
catch (err) {
|
|
2994
|
+
setActionNotice(`Could not verify permissions (${err instanceof Error ? err.message : "unknown error"}). Use "Skip for Now" to continue.`, "error", 5200);
|
|
2995
|
+
return;
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
// Advance to next step
|
|
2999
|
+
const currentIndex = STEP_ORDER.indexOf(onboardingStep);
|
|
3000
|
+
if (currentIndex < STEP_ORDER.length - 1) {
|
|
3001
|
+
setOnboardingStep(STEP_ORDER[currentIndex + 1]);
|
|
3002
|
+
}
|
|
3003
|
+
}, [
|
|
3004
|
+
onboardingStep,
|
|
3005
|
+
onboardingStyle,
|
|
3006
|
+
onboardingOptions,
|
|
3007
|
+
setActionNotice,
|
|
3008
|
+
handleOnboardingFinish,
|
|
3009
|
+
]);
|
|
3010
|
+
const handleOnboardingBack = useCallback(() => {
|
|
3011
|
+
const STEP_ORDER = [
|
|
3012
|
+
"wakeUp",
|
|
3013
|
+
"connection",
|
|
3014
|
+
"rpc",
|
|
3015
|
+
"senses",
|
|
3016
|
+
"activate",
|
|
3017
|
+
];
|
|
3018
|
+
const currentIndex = STEP_ORDER.indexOf(onboardingStep);
|
|
3019
|
+
if (currentIndex > 0) {
|
|
3020
|
+
setOnboardingStep(STEP_ORDER[currentIndex - 1]);
|
|
3021
|
+
}
|
|
3022
|
+
}, [onboardingStep]);
|
|
3023
|
+
const handleOnboardingUseLocalBackend = useCallback(() => {
|
|
3024
|
+
client.setBaseUrl(null);
|
|
3025
|
+
client.setToken(null);
|
|
3026
|
+
setOnboardingRemoteConnecting(false);
|
|
3027
|
+
setOnboardingRemoteError(null);
|
|
3028
|
+
setOnboardingRemoteConnected(false);
|
|
3029
|
+
setOnboardingRemoteApiBase("");
|
|
3030
|
+
setOnboardingRemoteToken("");
|
|
3031
|
+
setOnboardingCloudProvider("");
|
|
3032
|
+
setOnboardingRunMode("");
|
|
3033
|
+
retryStartup();
|
|
3034
|
+
}, [retryStartup]);
|
|
3035
|
+
const handleOnboardingRemoteConnect = useCallback(async () => {
|
|
3036
|
+
if (onboardingRemoteConnecting)
|
|
3037
|
+
return;
|
|
3038
|
+
let normalizedBase = "";
|
|
3039
|
+
try {
|
|
3040
|
+
normalizedBase = normalizeRemoteApiBaseInput(onboardingRemoteApiBase);
|
|
3041
|
+
}
|
|
3042
|
+
catch (err) {
|
|
3043
|
+
setOnboardingRemoteError(err instanceof Error ? err.message : "Enter a valid backend address.");
|
|
3044
|
+
return;
|
|
3045
|
+
}
|
|
3046
|
+
const accessKey = onboardingRemoteToken.trim();
|
|
3047
|
+
const probe = new MiladyClient(normalizedBase, accessKey || undefined);
|
|
3048
|
+
setOnboardingRemoteConnecting(true);
|
|
3049
|
+
setOnboardingRemoteError(null);
|
|
3050
|
+
try {
|
|
3051
|
+
const auth = await probe.getAuthStatus();
|
|
3052
|
+
if (auth.required && !accessKey) {
|
|
3053
|
+
throw new Error("This backend requires an access key.");
|
|
3054
|
+
}
|
|
3055
|
+
await probe.getOnboardingStatus();
|
|
3056
|
+
client.setBaseUrl(normalizedBase);
|
|
3057
|
+
client.setToken(accessKey || null);
|
|
3058
|
+
setOnboardingRunMode("cloud");
|
|
3059
|
+
setOnboardingCloudProvider("remote");
|
|
3060
|
+
setOnboardingRemoteApiBase(normalizedBase);
|
|
3061
|
+
setOnboardingRemoteToken(accessKey);
|
|
3062
|
+
setOnboardingRemoteConnected(true);
|
|
3063
|
+
setActionNotice("Connected to remote Milady backend.", "success", 4200);
|
|
3064
|
+
retryStartup();
|
|
3065
|
+
}
|
|
3066
|
+
catch (err) {
|
|
3067
|
+
const message = err instanceof Error ? err.message : "Failed to reach remote backend.";
|
|
3068
|
+
const normalizedMessage = /401|unauthorized|forbidden/i.test(message) && accessKey
|
|
3069
|
+
? "Access key rejected. Check the address and try again."
|
|
3070
|
+
: message;
|
|
3071
|
+
setOnboardingRemoteError(normalizedMessage);
|
|
3072
|
+
}
|
|
3073
|
+
finally {
|
|
3074
|
+
setOnboardingRemoteConnecting(false);
|
|
3075
|
+
}
|
|
3076
|
+
}, [
|
|
3077
|
+
onboardingRemoteApiBase,
|
|
3078
|
+
onboardingRemoteConnecting,
|
|
3079
|
+
onboardingRemoteToken,
|
|
3080
|
+
retryStartup,
|
|
3081
|
+
setActionNotice,
|
|
3082
|
+
]);
|
|
3083
|
+
// ── Cloud ──────────────────────────────────────────────────────────
|
|
3084
|
+
const handleCloudLogin = useCallback(async () => {
|
|
3085
|
+
if (elizaCloudLoginBusyRef.current || elizaCloudLoginBusy)
|
|
3086
|
+
return;
|
|
3087
|
+
elizaCloudLoginBusyRef.current = true;
|
|
3088
|
+
setElizaCloudLoginBusy(true);
|
|
3089
|
+
setElizaCloudLoginError(null);
|
|
3090
|
+
try {
|
|
3091
|
+
const resp = await client.cloudLogin();
|
|
3092
|
+
if (!resp.ok) {
|
|
3093
|
+
setElizaCloudLoginError(resp.error || "Failed to start Eliza Cloud login");
|
|
3094
|
+
elizaCloudLoginBusyRef.current = false;
|
|
3095
|
+
setElizaCloudLoginBusy(false);
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
3098
|
+
// Try to open the login URL in the system browser (uses desktop bridge
|
|
3099
|
+
// in Electrobun, falls back to window.open in web contexts).
|
|
3100
|
+
if (resp.browserUrl) {
|
|
3101
|
+
try {
|
|
3102
|
+
await openExternalUrl(resp.browserUrl);
|
|
3103
|
+
}
|
|
3104
|
+
catch {
|
|
3105
|
+
// Popup was blocked (common when window.open runs after an async
|
|
3106
|
+
// gap and loses user-gesture context). Surface the URL so the user
|
|
3107
|
+
// can open it manually — the polling loop below still runs.
|
|
3108
|
+
setElizaCloudLoginError(`Open this link to log in: ${resp.browserUrl}`);
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
let pollInFlight = false;
|
|
3112
|
+
let consecutivePollErrors = 0;
|
|
3113
|
+
const pollDeadline = Date.now() + ELIZA_CLOUD_LOGIN_TIMEOUT_MS;
|
|
3114
|
+
const stopCloudLoginPolling = (error = null) => {
|
|
3115
|
+
if (elizaCloudLoginPollTimer.current !== null) {
|
|
3116
|
+
clearInterval(elizaCloudLoginPollTimer.current);
|
|
3117
|
+
elizaCloudLoginPollTimer.current = null;
|
|
3118
|
+
}
|
|
3119
|
+
elizaCloudLoginBusyRef.current = false;
|
|
3120
|
+
setElizaCloudLoginBusy(false);
|
|
3121
|
+
if (error !== null) {
|
|
3122
|
+
setElizaCloudLoginError(error);
|
|
3123
|
+
}
|
|
3124
|
+
};
|
|
3125
|
+
// Start polling
|
|
3126
|
+
elizaCloudLoginPollTimer.current = window.setInterval(async () => {
|
|
3127
|
+
if (!elizaCloudLoginPollTimer.current || pollInFlight)
|
|
3128
|
+
return;
|
|
3129
|
+
if (Date.now() >= pollDeadline) {
|
|
3130
|
+
stopCloudLoginPolling("Eliza Cloud login timed out. Please try again.");
|
|
3131
|
+
return;
|
|
3132
|
+
}
|
|
3133
|
+
pollInFlight = true;
|
|
3134
|
+
try {
|
|
3135
|
+
if (!elizaCloudLoginPollTimer.current)
|
|
3136
|
+
return;
|
|
3137
|
+
const poll = await client.cloudLoginPoll(resp.sessionId);
|
|
3138
|
+
if (!elizaCloudLoginPollTimer.current)
|
|
3139
|
+
return;
|
|
3140
|
+
consecutivePollErrors = 0;
|
|
3141
|
+
if (poll.status === "authenticated") {
|
|
3142
|
+
stopCloudLoginPolling();
|
|
3143
|
+
setElizaCloudConnected(true);
|
|
3144
|
+
setElizaCloudEnabled(true);
|
|
3145
|
+
setElizaCloudLoginError(null);
|
|
3146
|
+
setActionNotice("Logged in to Eliza Cloud successfully.", "success", 6000);
|
|
3147
|
+
void loadWalletConfig();
|
|
3148
|
+
// Delay the credit fetch slightly so the backend has time to
|
|
3149
|
+
// persist the API key before we query cloud status / credits.
|
|
3150
|
+
setTimeout(() => void pollCloudCredits(), 2000);
|
|
3151
|
+
}
|
|
3152
|
+
else if (poll.status === "expired" || poll.status === "error") {
|
|
3153
|
+
stopCloudLoginPolling(poll.error ?? "Login session expired. Please try again.");
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
catch (pollErr) {
|
|
3157
|
+
console.error("Eliza Cloud login poll error:", pollErr);
|
|
3158
|
+
if (!elizaCloudLoginPollTimer.current)
|
|
3159
|
+
return;
|
|
3160
|
+
consecutivePollErrors += 1;
|
|
3161
|
+
if (consecutivePollErrors >= ELIZA_CLOUD_LOGIN_MAX_CONSECUTIVE_ERRORS) {
|
|
3162
|
+
const detail = pollErr instanceof Error && pollErr.message
|
|
3163
|
+
? ` Last error: ${pollErr.message}`
|
|
3164
|
+
: "";
|
|
3165
|
+
stopCloudLoginPolling(`Eliza Cloud login check failed after repeated errors.${detail}`);
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
finally {
|
|
3169
|
+
pollInFlight = false;
|
|
3170
|
+
}
|
|
3171
|
+
}, ELIZA_CLOUD_LOGIN_POLL_INTERVAL_MS);
|
|
3172
|
+
}
|
|
3173
|
+
catch (err) {
|
|
3174
|
+
setElizaCloudLoginError(err instanceof Error ? err.message : "Eliza Cloud login failed");
|
|
3175
|
+
elizaCloudLoginBusyRef.current = false;
|
|
3176
|
+
setElizaCloudLoginBusy(false);
|
|
3177
|
+
}
|
|
3178
|
+
}, [
|
|
3179
|
+
elizaCloudLoginBusy,
|
|
3180
|
+
setActionNotice,
|
|
3181
|
+
pollCloudCredits,
|
|
3182
|
+
loadWalletConfig,
|
|
3183
|
+
]);
|
|
3184
|
+
const handleCloudDisconnect = useCallback(async () => {
|
|
3185
|
+
if (!(await confirmDesktopAction({
|
|
3186
|
+
title: "Disconnect from Eliza Cloud",
|
|
3187
|
+
message: "The agent will need a local AI provider to continue working.",
|
|
3188
|
+
confirmLabel: "Disconnect",
|
|
3189
|
+
cancelLabel: "Cancel",
|
|
3190
|
+
type: "warning",
|
|
3191
|
+
})))
|
|
3192
|
+
return;
|
|
3193
|
+
setElizaCloudDisconnecting(true);
|
|
3194
|
+
try {
|
|
3195
|
+
await client.cloudDisconnect();
|
|
3196
|
+
setElizaCloudEnabled(false);
|
|
3197
|
+
setElizaCloudConnected(false);
|
|
3198
|
+
setElizaCloudCredits(null);
|
|
3199
|
+
setElizaCloudUserId(null);
|
|
3200
|
+
setActionNotice("Disconnected from Eliza Cloud.", "success");
|
|
3201
|
+
}
|
|
3202
|
+
catch (err) {
|
|
3203
|
+
setActionNotice(`Failed to disconnect: ${err instanceof Error ? err.message : err}`, "error");
|
|
3204
|
+
}
|
|
3205
|
+
finally {
|
|
3206
|
+
setElizaCloudDisconnecting(false);
|
|
3207
|
+
}
|
|
3208
|
+
}, [setActionNotice]);
|
|
3209
|
+
// ── Updates ────────────────────────────────────────────────────────
|
|
3210
|
+
const handleChannelChange = useCallback(async (channel) => {
|
|
3211
|
+
if (updateChannelSavingRef.current || updateChannelSaving)
|
|
3212
|
+
return;
|
|
3213
|
+
if (updateStatus?.channel === channel)
|
|
3214
|
+
return;
|
|
3215
|
+
updateChannelSavingRef.current = true;
|
|
3216
|
+
setUpdateChannelSaving(true);
|
|
3217
|
+
try {
|
|
3218
|
+
await client.setUpdateChannel(channel);
|
|
3219
|
+
await loadUpdateStatus(true);
|
|
3220
|
+
}
|
|
3221
|
+
catch {
|
|
3222
|
+
/* ignore */
|
|
3223
|
+
}
|
|
3224
|
+
finally {
|
|
3225
|
+
updateChannelSavingRef.current = false;
|
|
3226
|
+
setUpdateChannelSaving(false);
|
|
3227
|
+
}
|
|
3228
|
+
}, [updateChannelSaving, updateStatus, loadUpdateStatus]);
|
|
3229
|
+
// ── Agent export/import ────────────────────────────────────────────
|
|
3230
|
+
const handleAgentExport = useCallback(async () => {
|
|
3231
|
+
if (exportBusyRef.current || exportBusy)
|
|
3232
|
+
return;
|
|
3233
|
+
if (!exportPassword) {
|
|
3234
|
+
setExportError("Password is required.");
|
|
3235
|
+
setExportSuccess(null);
|
|
3236
|
+
return;
|
|
3237
|
+
}
|
|
3238
|
+
if (exportPassword.length < AGENT_TRANSFER_MIN_PASSWORD_LENGTH) {
|
|
3239
|
+
setExportError(`Password must be at least ${AGENT_TRANSFER_MIN_PASSWORD_LENGTH} characters.`);
|
|
3240
|
+
setExportSuccess(null);
|
|
3241
|
+
return;
|
|
3242
|
+
}
|
|
3243
|
+
try {
|
|
3244
|
+
exportBusyRef.current = true;
|
|
3245
|
+
setExportBusy(true);
|
|
3246
|
+
setExportError(null);
|
|
3247
|
+
setExportSuccess(null);
|
|
3248
|
+
const resp = await client.exportAgent(exportPassword, exportIncludeLogs);
|
|
3249
|
+
const blob = await resp.blob();
|
|
3250
|
+
const disposition = resp.headers.get("Content-Disposition") ?? "";
|
|
3251
|
+
const filenameMatch = /filename="?([^"]+)"?/.exec(disposition);
|
|
3252
|
+
const filename = filenameMatch?.[1] ?? "agent-export.eliza-agent";
|
|
3253
|
+
const url = URL.createObjectURL(blob);
|
|
3254
|
+
const a = document.createElement("a");
|
|
3255
|
+
a.href = url;
|
|
3256
|
+
a.download = filename;
|
|
3257
|
+
document.body.appendChild(a);
|
|
3258
|
+
a.click();
|
|
3259
|
+
document.body.removeChild(a);
|
|
3260
|
+
URL.revokeObjectURL(url);
|
|
3261
|
+
setExportSuccess(`Exported successfully (${(blob.size / 1024).toFixed(0)} KB)`);
|
|
3262
|
+
setExportPassword("");
|
|
3263
|
+
}
|
|
3264
|
+
catch (err) {
|
|
3265
|
+
setExportError(err instanceof Error ? err.message : "Export failed");
|
|
3266
|
+
}
|
|
3267
|
+
finally {
|
|
3268
|
+
exportBusyRef.current = false;
|
|
3269
|
+
setExportBusy(false);
|
|
3270
|
+
}
|
|
3271
|
+
}, [exportBusy, exportPassword, exportIncludeLogs]);
|
|
3272
|
+
const handleAgentImport = useCallback(async () => {
|
|
3273
|
+
if (importBusyRef.current || importBusy)
|
|
3274
|
+
return;
|
|
3275
|
+
if (!importFile) {
|
|
3276
|
+
setImportError("Select an export file before importing.");
|
|
3277
|
+
setImportSuccess(null);
|
|
3278
|
+
return;
|
|
3279
|
+
}
|
|
3280
|
+
if (!importPassword) {
|
|
3281
|
+
setImportError("Password is required.");
|
|
3282
|
+
setImportSuccess(null);
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
3285
|
+
if (importPassword.length < AGENT_TRANSFER_MIN_PASSWORD_LENGTH) {
|
|
3286
|
+
setImportError(`Password must be at least ${AGENT_TRANSFER_MIN_PASSWORD_LENGTH} characters.`);
|
|
3287
|
+
setImportSuccess(null);
|
|
3288
|
+
return;
|
|
3289
|
+
}
|
|
3290
|
+
try {
|
|
3291
|
+
importBusyRef.current = true;
|
|
3292
|
+
setImportBusy(true);
|
|
3293
|
+
setImportError(null);
|
|
3294
|
+
setImportSuccess(null);
|
|
3295
|
+
const fileBuffer = await importFile.arrayBuffer();
|
|
3296
|
+
const result = await client.importAgent(importPassword, fileBuffer);
|
|
3297
|
+
const counts = result.counts;
|
|
3298
|
+
const summary = [
|
|
3299
|
+
counts.memories ? `${counts.memories} memories` : null,
|
|
3300
|
+
counts.entities ? `${counts.entities} entities` : null,
|
|
3301
|
+
counts.rooms ? `${counts.rooms} rooms` : null,
|
|
3302
|
+
]
|
|
3303
|
+
.filter(Boolean)
|
|
3304
|
+
.join(", ");
|
|
3305
|
+
setImportSuccess(`Imported "${result.agentName}" successfully: ${summary || "no data"}. Restart the agent to activate.`);
|
|
3306
|
+
setImportPassword("");
|
|
3307
|
+
setImportFile(null);
|
|
3308
|
+
}
|
|
3309
|
+
catch (err) {
|
|
3310
|
+
setImportError(err instanceof Error ? err.message : "Import failed");
|
|
3311
|
+
}
|
|
3312
|
+
finally {
|
|
3313
|
+
importBusyRef.current = false;
|
|
3314
|
+
setImportBusy(false);
|
|
3315
|
+
}
|
|
3316
|
+
}, [importBusy, importFile, importPassword]);
|
|
3317
|
+
// ── Emote picker ────────────────────────────────────────────────────
|
|
3318
|
+
const closeCommandPalette = useCallback(() => {
|
|
3319
|
+
_setCommandPaletteOpen(false);
|
|
3320
|
+
setCommandQuery("");
|
|
3321
|
+
setCommandActiveIndex(0);
|
|
3322
|
+
}, []);
|
|
3323
|
+
const openEmotePicker = useCallback(() => {
|
|
3324
|
+
setEmotePickerOpen(true);
|
|
3325
|
+
}, []);
|
|
3326
|
+
const closeEmotePicker = useCallback(() => {
|
|
3327
|
+
setEmotePickerOpen(false);
|
|
3328
|
+
}, []);
|
|
3329
|
+
// ── Generic state setter ───────────────────────────────────────────
|
|
3330
|
+
const setState = useCallback((key, value) => {
|
|
3331
|
+
const setterMap = {
|
|
3332
|
+
tab: setTabRaw,
|
|
3333
|
+
chatInput: setChatInput,
|
|
3334
|
+
chatAvatarVisible: setChatAvatarVisible,
|
|
3335
|
+
chatAgentVoiceMuted: setChatAgentVoiceMuted,
|
|
3336
|
+
chatLastUsage: setChatLastUsage,
|
|
3337
|
+
chatMode: setChatMode,
|
|
3338
|
+
chatAvatarSpeaking: setChatAvatarSpeaking,
|
|
3339
|
+
companionMessageCutoffTs: setCompanionMessageCutoffTs,
|
|
3340
|
+
uiShellMode: setUiShellModeState,
|
|
3341
|
+
uiLanguage: setUiLanguageState,
|
|
3342
|
+
autonomousRunHealthByRunId: setAutonomousRunHealthByRunId,
|
|
3343
|
+
startupError: setStartupError,
|
|
3344
|
+
pairingCodeInput: setPairingCodeInput,
|
|
3345
|
+
pluginFilter: setPluginFilter,
|
|
3346
|
+
pluginStatusFilter: setPluginStatusFilter,
|
|
3347
|
+
pluginSearch: setPluginSearch,
|
|
3348
|
+
pluginSettingsOpen: setPluginSettingsOpen,
|
|
3349
|
+
pluginAdvancedOpen: setPluginAdvancedOpen,
|
|
3350
|
+
skillsSubTab: setSkillsSubTab,
|
|
3351
|
+
skillCreateFormOpen: setSkillCreateFormOpen,
|
|
3352
|
+
skillCreateName: setSkillCreateName,
|
|
3353
|
+
skillCreateDescription: setSkillCreateDescription,
|
|
3354
|
+
skillsMarketplaceQuery: setSkillsMarketplaceQuery,
|
|
3355
|
+
skillsMarketplaceManualGithubUrl: setSkillsMarketplaceManualGithubUrl,
|
|
3356
|
+
logTagFilter: setLogTagFilter,
|
|
3357
|
+
logLevelFilter: setLogLevelFilter,
|
|
3358
|
+
logSourceFilter: setLogSourceFilter,
|
|
3359
|
+
inventoryView: setInventoryView,
|
|
3360
|
+
inventorySort: setInventorySort,
|
|
3361
|
+
inventoryChainFocus: setInventoryChainFocus,
|
|
3362
|
+
exportPassword: setExportPassword,
|
|
3363
|
+
exportIncludeLogs: setExportIncludeLogs,
|
|
3364
|
+
exportError: setExportError,
|
|
3365
|
+
exportSuccess: setExportSuccess,
|
|
3366
|
+
importPassword: setImportPassword,
|
|
3367
|
+
importFile: setImportFile,
|
|
3368
|
+
importError: setImportError,
|
|
3369
|
+
importSuccess: setImportSuccess,
|
|
3370
|
+
onboardingName: setOnboardingName,
|
|
3371
|
+
onboardingOwnerName: setOnboardingOwnerName,
|
|
3372
|
+
onboardingStyle: setOnboardingStyle,
|
|
3373
|
+
onboardingRunMode: setOnboardingRunMode,
|
|
3374
|
+
onboardingCloudProvider: setOnboardingCloudProvider,
|
|
3375
|
+
onboardingSmallModel: setOnboardingSmallModel,
|
|
3376
|
+
onboardingLargeModel: setOnboardingLargeModel,
|
|
3377
|
+
onboardingProvider: setOnboardingProvider,
|
|
3378
|
+
onboardingApiKey: setOnboardingApiKey,
|
|
3379
|
+
onboardingRemoteApiBase: setOnboardingRemoteApiBase,
|
|
3380
|
+
onboardingRemoteToken: setOnboardingRemoteToken,
|
|
3381
|
+
onboardingRemoteConnecting: setOnboardingRemoteConnecting,
|
|
3382
|
+
onboardingRemoteError: setOnboardingRemoteError,
|
|
3383
|
+
onboardingRemoteConnected: setOnboardingRemoteConnected,
|
|
3384
|
+
onboardingSelectedChains: setOnboardingSelectedChains,
|
|
3385
|
+
onboardingRpcSelections: setOnboardingRpcSelections,
|
|
3386
|
+
onboardingOpenRouterModel: setOnboardingOpenRouterModel,
|
|
3387
|
+
onboardingPrimaryModel: setOnboardingPrimaryModel,
|
|
3388
|
+
onboardingTelegramToken: setOnboardingTelegramToken,
|
|
3389
|
+
onboardingDiscordToken: setOnboardingDiscordToken,
|
|
3390
|
+
onboardingWhatsAppSessionPath: setOnboardingWhatsAppSessionPath,
|
|
3391
|
+
onboardingTwilioAccountSid: setOnboardingTwilioAccountSid,
|
|
3392
|
+
onboardingTwilioAuthToken: setOnboardingTwilioAuthToken,
|
|
3393
|
+
onboardingTwilioPhoneNumber: setOnboardingTwilioPhoneNumber,
|
|
3394
|
+
onboardingBlooioApiKey: setOnboardingBlooioApiKey,
|
|
3395
|
+
onboardingBlooioPhoneNumber: setOnboardingBlooioPhoneNumber,
|
|
3396
|
+
onboardingGithubToken: setOnboardingGithubToken,
|
|
3397
|
+
onboardingSubscriptionTab: setOnboardingSubscriptionTab,
|
|
3398
|
+
onboardingElizaCloudTab: setOnboardingElizaCloudTab,
|
|
3399
|
+
onboardingRpcKeys: setOnboardingRpcKeys,
|
|
3400
|
+
onboardingAvatar: setOnboardingAvatar,
|
|
3401
|
+
onboardingRestarting: setOnboardingRestarting,
|
|
3402
|
+
elizaCloudEnabled: setElizaCloudEnabled,
|
|
3403
|
+
cloudDashboardView: setCloudDashboardView,
|
|
3404
|
+
selectedVrmIndex: setSelectedVrmIndex,
|
|
3405
|
+
customVrmUrl: setCustomVrmUrl,
|
|
3406
|
+
customBackgroundUrl: setCustomBackgroundUrl,
|
|
3407
|
+
commandQuery: setCommandQuery,
|
|
3408
|
+
commandActiveIndex: setCommandActiveIndex,
|
|
3409
|
+
emotePickerOpen: setEmotePickerOpen,
|
|
3410
|
+
storeSearch: setStoreSearch,
|
|
3411
|
+
storeFilter: setStoreFilter,
|
|
3412
|
+
storeSubTab: setStoreSubTab,
|
|
3413
|
+
catalogSearch: setCatalogSearch,
|
|
3414
|
+
catalogSort: setCatalogSort,
|
|
3415
|
+
catalogPage: setCatalogPage,
|
|
3416
|
+
skillReviewId: setSkillReviewId,
|
|
3417
|
+
skillReviewReport: setSkillReviewReport,
|
|
3418
|
+
activeGameApp: setActiveGameApp,
|
|
3419
|
+
activeGameDisplayName: setActiveGameDisplayName,
|
|
3420
|
+
activeGameViewerUrl: setActiveGameViewerUrl,
|
|
3421
|
+
activeGameSandbox: setActiveGameSandbox,
|
|
3422
|
+
activeGamePostMessageAuth: setActiveGamePostMessageAuth,
|
|
3423
|
+
activeGamePostMessagePayload: setActiveGamePostMessagePayload,
|
|
3424
|
+
gameOverlayEnabled: setGameOverlayEnabled,
|
|
3425
|
+
storePlugins: setStorePlugins,
|
|
3426
|
+
storeLoading: setStoreLoading,
|
|
3427
|
+
storeInstalling: setStoreInstalling,
|
|
3428
|
+
storeUninstalling: setStoreUninstalling,
|
|
3429
|
+
storeError: setStoreError,
|
|
3430
|
+
storeDetailPlugin: setStoreDetailPlugin,
|
|
3431
|
+
catalogSkills: setCatalogSkills,
|
|
3432
|
+
catalogTotal: setCatalogTotal,
|
|
3433
|
+
catalogTotalPages: setCatalogTotalPages,
|
|
3434
|
+
catalogLoading: setCatalogLoading,
|
|
3435
|
+
catalogError: setCatalogError,
|
|
3436
|
+
catalogDetailSkill: setCatalogDetailSkill,
|
|
3437
|
+
catalogInstalling: setCatalogInstalling,
|
|
3438
|
+
catalogUninstalling: setCatalogUninstalling,
|
|
3439
|
+
mcpConfiguredServers: setMcpConfiguredServers,
|
|
3440
|
+
mcpServerStatuses: setMcpServerStatuses,
|
|
3441
|
+
mcpMarketplaceQuery: setMcpMarketplaceQuery,
|
|
3442
|
+
mcpMarketplaceResults: setMcpMarketplaceResults,
|
|
3443
|
+
mcpMarketplaceLoading: setMcpMarketplaceLoading,
|
|
3444
|
+
mcpAction: setMcpAction,
|
|
3445
|
+
mcpAddingServer: setMcpAddingServer,
|
|
3446
|
+
mcpAddingResult: setMcpAddingResult,
|
|
3447
|
+
mcpEnvInputs: setMcpEnvInputs,
|
|
3448
|
+
mcpHeaderInputs: setMcpHeaderInputs,
|
|
3449
|
+
droppedFiles: setDroppedFiles,
|
|
3450
|
+
shareIngestNotice: setShareIngestNotice,
|
|
3451
|
+
appsSubTab: setAppsSubTab,
|
|
3452
|
+
agentSubTab: setAgentSubTab,
|
|
3453
|
+
pluginsSubTab: setPluginsSubTab,
|
|
3454
|
+
databaseSubTab: setDatabaseSubTab,
|
|
3455
|
+
configRaw: setConfigRaw,
|
|
3456
|
+
configText: setConfigText,
|
|
3457
|
+
};
|
|
3458
|
+
const setter = setterMap[key];
|
|
3459
|
+
if (setter)
|
|
3460
|
+
setter(value);
|
|
3461
|
+
}, [setSelectedVrmIndex]);
|
|
3462
|
+
// ── Initialization ─────────────────────────────────────────────────
|
|
3463
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: t is stable but defined later
|
|
3464
|
+
useEffect(() => {
|
|
3465
|
+
const startupRunId = startupRetryNonce;
|
|
3466
|
+
let unbindStatus = null;
|
|
3467
|
+
let unbindAgentEvents = null;
|
|
3468
|
+
let unbindHeartbeatEvents = null;
|
|
3469
|
+
let unbindEmotes = null;
|
|
3470
|
+
let unbindProactiveMessages = null;
|
|
3471
|
+
let handleVisibilityRef = null;
|
|
3472
|
+
let unbindWsReconnect = null;
|
|
3473
|
+
let unbindSystemWarnings = null;
|
|
3474
|
+
let unbindRestartRequired = null;
|
|
3475
|
+
let cancelled = false;
|
|
3476
|
+
const describeBackendFailure = (err, timedOut) => {
|
|
3477
|
+
const apiErr = asApiLikeError(err);
|
|
3478
|
+
if (apiErr?.kind === "http" && apiErr.status === 404) {
|
|
3479
|
+
return {
|
|
3480
|
+
reason: "backend-unreachable",
|
|
3481
|
+
phase: "starting-backend",
|
|
3482
|
+
message: "Backend API routes are unavailable on this origin (received 404).",
|
|
3483
|
+
detail: formatStartupErrorDetail(err),
|
|
3484
|
+
status: apiErr.status,
|
|
3485
|
+
path: apiErr.path,
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3488
|
+
if (timedOut || apiErr?.kind === "timeout") {
|
|
3489
|
+
return {
|
|
3490
|
+
reason: "backend-timeout",
|
|
3491
|
+
phase: "starting-backend",
|
|
3492
|
+
message: `Backend did not become reachable within ${Math.round(getBackendStartupTimeoutMs() / 1000)}s.`,
|
|
3493
|
+
detail: formatStartupErrorDetail(err),
|
|
3494
|
+
status: apiErr?.status,
|
|
3495
|
+
path: apiErr?.path,
|
|
3496
|
+
};
|
|
3497
|
+
}
|
|
3498
|
+
return {
|
|
3499
|
+
reason: "backend-unreachable",
|
|
3500
|
+
phase: "starting-backend",
|
|
3501
|
+
message: "Failed to reach backend during startup.",
|
|
3502
|
+
detail: formatStartupErrorDetail(err),
|
|
3503
|
+
status: apiErr?.status,
|
|
3504
|
+
path: apiErr?.path,
|
|
3505
|
+
};
|
|
3506
|
+
};
|
|
3507
|
+
const describeAgentFailure = (err, timedOut, diagnostics) => {
|
|
3508
|
+
const detail = diagnostics?.lastError ||
|
|
3509
|
+
formatStartupErrorDetail(err) ||
|
|
3510
|
+
"Agent runtime did not report a reason.";
|
|
3511
|
+
const isAssetMissing = /required companion assets could not be loaded|bundled avatar .* could not be loaded/i.test(detail);
|
|
3512
|
+
if (!timedOut && isAssetMissing) {
|
|
3513
|
+
return {
|
|
3514
|
+
reason: "asset-missing",
|
|
3515
|
+
phase: "initializing-agent",
|
|
3516
|
+
message: "Required companion assets could not be loaded.",
|
|
3517
|
+
detail,
|
|
3518
|
+
};
|
|
3519
|
+
}
|
|
3520
|
+
if (timedOut) {
|
|
3521
|
+
return {
|
|
3522
|
+
reason: "agent-timeout",
|
|
3523
|
+
phase: "initializing-agent",
|
|
3524
|
+
message: `Agent did not reach running or paused within ${Math.round(AGENT_READY_TIMEOUT_MS / 1000)}s.`,
|
|
3525
|
+
detail,
|
|
3526
|
+
};
|
|
3527
|
+
}
|
|
3528
|
+
return {
|
|
3529
|
+
reason: "agent-error",
|
|
3530
|
+
phase: "initializing-agent",
|
|
3531
|
+
message: "Agent runtime reported a startup error.",
|
|
3532
|
+
detail,
|
|
3533
|
+
};
|
|
3534
|
+
};
|
|
3535
|
+
const STARTUP_WARN_PREFIX = "[milady][startup:init]";
|
|
3536
|
+
const logStartupWarning = (scope, err) => {
|
|
3537
|
+
console.warn(`${STARTUP_WARN_PREFIX} ${scope}`, err);
|
|
3538
|
+
};
|
|
3539
|
+
const initApp = async () => {
|
|
3540
|
+
if (import.meta.env.DEV && startupRunId > 0) {
|
|
3541
|
+
console.debug(`[milady] Retrying startup run #${startupRunId}`);
|
|
3542
|
+
}
|
|
3543
|
+
const BASE_DELAY_MS = 250;
|
|
3544
|
+
const MAX_DELAY_MS = 1000;
|
|
3545
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3546
|
+
let onboardingNeedsOptions = false;
|
|
3547
|
+
let requiresAuth = false;
|
|
3548
|
+
let latestAuth = {
|
|
3549
|
+
required: false,
|
|
3550
|
+
pairingEnabled: false,
|
|
3551
|
+
expiresAt: null,
|
|
3552
|
+
};
|
|
3553
|
+
setStartupError(null);
|
|
3554
|
+
setStartupPhase("starting-backend");
|
|
3555
|
+
setAuthRequired(false);
|
|
3556
|
+
setConnected(false);
|
|
3557
|
+
const backendStartedAt = Date.now();
|
|
3558
|
+
let lastBackendError = null;
|
|
3559
|
+
// Keep the splash screen up until the backend is reachable.
|
|
3560
|
+
let backendAttempts = 0;
|
|
3561
|
+
while (!cancelled) {
|
|
3562
|
+
if (Date.now() - backendStartedAt >= getBackendStartupTimeoutMs()) {
|
|
3563
|
+
setStartupError(describeBackendFailure(lastBackendError, true));
|
|
3564
|
+
setOnboardingLoading(false);
|
|
3565
|
+
return;
|
|
3566
|
+
}
|
|
3567
|
+
try {
|
|
3568
|
+
const auth = await client.getAuthStatus();
|
|
3569
|
+
latestAuth = auth;
|
|
3570
|
+
if (auth.required && !client.hasToken()) {
|
|
3571
|
+
setAuthRequired(true);
|
|
3572
|
+
setPairingEnabled(auth.pairingEnabled);
|
|
3573
|
+
setPairingExpiresAt(auth.expiresAt);
|
|
3574
|
+
requiresAuth = true;
|
|
3575
|
+
break;
|
|
3576
|
+
}
|
|
3577
|
+
const { complete } = await client.getOnboardingStatus();
|
|
3578
|
+
setOnboardingComplete(complete);
|
|
3579
|
+
onboardingNeedsOptions = !complete;
|
|
3580
|
+
break;
|
|
3581
|
+
}
|
|
3582
|
+
catch (err) {
|
|
3583
|
+
const apiErr = asApiLikeError(err);
|
|
3584
|
+
if (apiErr?.status === 401 && client.hasToken()) {
|
|
3585
|
+
client.setToken(null);
|
|
3586
|
+
setAuthRequired(true);
|
|
3587
|
+
setPairingEnabled(latestAuth.pairingEnabled);
|
|
3588
|
+
setPairingExpiresAt(latestAuth.expiresAt);
|
|
3589
|
+
requiresAuth = true;
|
|
3590
|
+
break;
|
|
3591
|
+
}
|
|
3592
|
+
if (apiErr?.status === 404) {
|
|
3593
|
+
setStartupError(describeBackendFailure(err, false));
|
|
3594
|
+
setOnboardingLoading(false);
|
|
3595
|
+
return;
|
|
3596
|
+
}
|
|
3597
|
+
lastBackendError = err;
|
|
3598
|
+
backendAttempts += 1;
|
|
3599
|
+
const delay = Math.min(BASE_DELAY_MS * 2 ** Math.min(backendAttempts, 2), MAX_DELAY_MS);
|
|
3600
|
+
await sleep(delay);
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
if (cancelled) {
|
|
3604
|
+
return;
|
|
3605
|
+
}
|
|
3606
|
+
if (requiresAuth) {
|
|
3607
|
+
setOnboardingLoading(false);
|
|
3608
|
+
return;
|
|
3609
|
+
}
|
|
3610
|
+
// On fresh installs, unblock to onboarding as soon as options are available.
|
|
3611
|
+
if (onboardingNeedsOptions) {
|
|
3612
|
+
const optionsStartedAt = Date.now();
|
|
3613
|
+
let optionsError = null;
|
|
3614
|
+
while (!cancelled) {
|
|
3615
|
+
if (Date.now() - optionsStartedAt >= getBackendStartupTimeoutMs()) {
|
|
3616
|
+
setStartupError(describeBackendFailure(optionsError, true));
|
|
3617
|
+
setOnboardingLoading(false);
|
|
3618
|
+
return;
|
|
3619
|
+
}
|
|
3620
|
+
try {
|
|
3621
|
+
const options = await client.getOnboardingOptions();
|
|
3622
|
+
setOnboardingOptions(options);
|
|
3623
|
+
setOnboardingLoading(false);
|
|
3624
|
+
return;
|
|
3625
|
+
}
|
|
3626
|
+
catch (err) {
|
|
3627
|
+
const apiErr = asApiLikeError(err);
|
|
3628
|
+
if (apiErr?.status === 401 && client.hasToken()) {
|
|
3629
|
+
client.setToken(null);
|
|
3630
|
+
setAuthRequired(true);
|
|
3631
|
+
setPairingEnabled(latestAuth.pairingEnabled);
|
|
3632
|
+
setPairingExpiresAt(latestAuth.expiresAt);
|
|
3633
|
+
setOnboardingLoading(false);
|
|
3634
|
+
return;
|
|
3635
|
+
}
|
|
3636
|
+
if (apiErr?.status === 404) {
|
|
3637
|
+
setStartupError(describeBackendFailure(err, false));
|
|
3638
|
+
setOnboardingLoading(false);
|
|
3639
|
+
return;
|
|
3640
|
+
}
|
|
3641
|
+
optionsError = err;
|
|
3642
|
+
await sleep(500);
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
return;
|
|
3646
|
+
}
|
|
3647
|
+
setStartupPhase("initializing-agent");
|
|
3648
|
+
// Existing installs: keep loading until the runtime reports ready.
|
|
3649
|
+
let agentReady = false;
|
|
3650
|
+
const agentDeadlineAt = Date.now() + AGENT_READY_TIMEOUT_MS;
|
|
3651
|
+
let lastAgentError = null;
|
|
3652
|
+
let lastAgentDiagnostics;
|
|
3653
|
+
while (!cancelled) {
|
|
3654
|
+
if (Date.now() >= agentDeadlineAt) {
|
|
3655
|
+
setStartupError(describeAgentFailure(lastAgentError, true, lastAgentDiagnostics));
|
|
3656
|
+
setOnboardingLoading(false);
|
|
3657
|
+
return;
|
|
3658
|
+
}
|
|
3659
|
+
try {
|
|
3660
|
+
let status = await client.getStatus();
|
|
3661
|
+
setAgentStatus(status);
|
|
3662
|
+
setConnected(true);
|
|
3663
|
+
lastAgentDiagnostics = status.startup;
|
|
3664
|
+
// Hydrate deferred restart state
|
|
3665
|
+
if (status.pendingRestart) {
|
|
3666
|
+
setPendingRestart(true);
|
|
3667
|
+
setPendingRestartReasons(status.pendingRestartReasons ?? []);
|
|
3668
|
+
}
|
|
3669
|
+
if (status.state === "not_started" || status.state === "stopped") {
|
|
3670
|
+
try {
|
|
3671
|
+
status = await client.startAgent();
|
|
3672
|
+
setAgentStatus(status);
|
|
3673
|
+
lastAgentDiagnostics = status.startup;
|
|
3674
|
+
}
|
|
3675
|
+
catch (err) {
|
|
3676
|
+
lastAgentError = err;
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
if (status.state === "running") {
|
|
3680
|
+
agentReady = true;
|
|
3681
|
+
break;
|
|
3682
|
+
}
|
|
3683
|
+
if (status.state === "error") {
|
|
3684
|
+
setStartupError(describeAgentFailure(lastAgentError, false, status.startup));
|
|
3685
|
+
setOnboardingLoading(false);
|
|
3686
|
+
return;
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
catch (err) {
|
|
3690
|
+
const apiErr = asApiLikeError(err);
|
|
3691
|
+
if (apiErr?.status === 401 && client.hasToken()) {
|
|
3692
|
+
client.setToken(null);
|
|
3693
|
+
setAuthRequired(true);
|
|
3694
|
+
setPairingEnabled(latestAuth.pairingEnabled);
|
|
3695
|
+
setPairingExpiresAt(latestAuth.expiresAt);
|
|
3696
|
+
setOnboardingLoading(false);
|
|
3697
|
+
return;
|
|
3698
|
+
}
|
|
3699
|
+
lastAgentError = err;
|
|
3700
|
+
setConnected(false);
|
|
3701
|
+
}
|
|
3702
|
+
await sleep(500);
|
|
3703
|
+
}
|
|
3704
|
+
if (cancelled)
|
|
3705
|
+
return;
|
|
3706
|
+
if (!agentReady) {
|
|
3707
|
+
setStartupError(describeAgentFailure(lastAgentError, true, lastAgentDiagnostics));
|
|
3708
|
+
setOnboardingLoading(false);
|
|
3709
|
+
return;
|
|
3710
|
+
}
|
|
3711
|
+
setStartupError(null);
|
|
3712
|
+
const greetConvId = await hydrateInitialConversationState();
|
|
3713
|
+
setStartupPhase("ready");
|
|
3714
|
+
setOnboardingLoading(false);
|
|
3715
|
+
if (greetConvId) {
|
|
3716
|
+
void requestGreetingWhenRunning(greetConvId, { showOverlay: true });
|
|
3717
|
+
}
|
|
3718
|
+
void loadWorkbench();
|
|
3719
|
+
void loadPlugins(); // Hydrate plugin state early so Nav sees streaming-base toggle
|
|
3720
|
+
// Hydrate coding agent sessions (also re-called on WS reconnect / server restart)
|
|
3721
|
+
const hydratePtySessions = () => {
|
|
3722
|
+
client
|
|
3723
|
+
.getCodingAgentStatus()
|
|
3724
|
+
.then((status) => {
|
|
3725
|
+
if (status?.tasks) {
|
|
3726
|
+
setPtySessions(mapServerTasksToSessions(status.tasks));
|
|
3727
|
+
}
|
|
3728
|
+
})
|
|
3729
|
+
.catch(() => { }); // non-critical
|
|
3730
|
+
};
|
|
3731
|
+
hydratePtySessions();
|
|
3732
|
+
let ptyHydratedViaWs = false;
|
|
3733
|
+
// Connect WebSocket
|
|
3734
|
+
client.connectWs();
|
|
3735
|
+
unbindEmotes = client.onWsEvent("emote", (data) => {
|
|
3736
|
+
const emote = normalizeAppEmoteEvent(data);
|
|
3737
|
+
if (emote) {
|
|
3738
|
+
dispatchAppEmoteEvent(emote);
|
|
3739
|
+
}
|
|
3740
|
+
});
|
|
3741
|
+
// Re-hydrate PTY sessions on WS reconnect — events sent during
|
|
3742
|
+
// the disconnect gap are lost, so we reconcile from the server.
|
|
3743
|
+
unbindWsReconnect = client.onWsEvent("ws-reconnected", () => {
|
|
3744
|
+
hydratePtySessions();
|
|
3745
|
+
});
|
|
3746
|
+
// Surface system-level warnings (connector failures, wiring exhaustion, etc.)
|
|
3747
|
+
unbindSystemWarnings = client.onWsEvent("system-warning", (data) => {
|
|
3748
|
+
const message = typeof data.message === "string" ? data.message : "";
|
|
3749
|
+
if (message) {
|
|
3750
|
+
setSystemWarnings((prev) => {
|
|
3751
|
+
if (prev.includes(message))
|
|
3752
|
+
return prev;
|
|
3753
|
+
const next = [...prev, message];
|
|
3754
|
+
if (next.length > 50)
|
|
3755
|
+
next.splice(0, next.length - 50);
|
|
3756
|
+
return next;
|
|
3757
|
+
});
|
|
3758
|
+
}
|
|
3759
|
+
});
|
|
3760
|
+
// Re-hydrate when the tab becomes visible — browsers may throttle
|
|
3761
|
+
// or drop WS messages for background tabs.
|
|
3762
|
+
handleVisibilityRef = () => {
|
|
3763
|
+
if (document.visibilityState === "visible") {
|
|
3764
|
+
hydratePtySessions();
|
|
3765
|
+
}
|
|
3766
|
+
};
|
|
3767
|
+
document.addEventListener("visibilitychange", handleVisibilityRef);
|
|
3768
|
+
unbindStatus = client.onWsEvent("status", (data) => {
|
|
3769
|
+
const nextStatus = parseAgentStatusEvent(data);
|
|
3770
|
+
if (nextStatus) {
|
|
3771
|
+
setAgentStatusIfChanged(nextStatus);
|
|
3772
|
+
// Auto-refresh plugins when agent reports a restart
|
|
3773
|
+
if (data.restarted) {
|
|
3774
|
+
setPendingRestart(false);
|
|
3775
|
+
setPendingRestartReasons([]);
|
|
3776
|
+
void loadPlugins();
|
|
3777
|
+
hydratePtySessions();
|
|
3778
|
+
ptyHydratedViaWs = true;
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
// Re-hydrate PTY sessions on first WS status event to close
|
|
3782
|
+
// the race between initial REST fetch and WS connection.
|
|
3783
|
+
if (!ptyHydratedViaWs) {
|
|
3784
|
+
ptyHydratedViaWs = true;
|
|
3785
|
+
hydratePtySessions();
|
|
3786
|
+
}
|
|
3787
|
+
// Sync pending restart state from periodic broadcasts
|
|
3788
|
+
// Guard with value comparison to avoid no-op re-renders
|
|
3789
|
+
if (typeof data.pendingRestart === "boolean") {
|
|
3790
|
+
setPendingRestart((prev) => prev === data.pendingRestart
|
|
3791
|
+
? prev
|
|
3792
|
+
: data.pendingRestart);
|
|
3793
|
+
}
|
|
3794
|
+
if (Array.isArray(data.pendingRestartReasons)) {
|
|
3795
|
+
const nextReasons = data.pendingRestartReasons.filter((el) => typeof el === "string");
|
|
3796
|
+
setPendingRestartReasons((prev) => {
|
|
3797
|
+
if (prev.length === nextReasons.length &&
|
|
3798
|
+
prev.every((r, i) => r === nextReasons[i])) {
|
|
3799
|
+
return prev; // identical — skip re-render
|
|
3800
|
+
}
|
|
3801
|
+
return nextReasons;
|
|
3802
|
+
});
|
|
3803
|
+
}
|
|
3804
|
+
});
|
|
3805
|
+
unbindRestartRequired = client.onWsEvent("restart-required", (data) => {
|
|
3806
|
+
if (Array.isArray(data.reasons)) {
|
|
3807
|
+
setPendingRestartReasons(data.reasons.filter((el) => typeof el === "string"));
|
|
3808
|
+
setPendingRestart(true);
|
|
3809
|
+
setRestartBannerDismissed(false);
|
|
3810
|
+
}
|
|
3811
|
+
});
|
|
3812
|
+
unbindAgentEvents = client.onWsEvent("agent_event", (data) => {
|
|
3813
|
+
const event = parseStreamEventEnvelopeEvent(data);
|
|
3814
|
+
if (event) {
|
|
3815
|
+
appendAutonomousEvent(event);
|
|
3816
|
+
}
|
|
3817
|
+
});
|
|
3818
|
+
unbindHeartbeatEvents = client.onWsEvent("heartbeat_event", (data) => {
|
|
3819
|
+
const event = parseStreamEventEnvelopeEvent(data);
|
|
3820
|
+
if (event) {
|
|
3821
|
+
appendAutonomousEvent(event);
|
|
3822
|
+
}
|
|
3823
|
+
});
|
|
3824
|
+
await fetchAutonomyReplay();
|
|
3825
|
+
// Handle proactive messages from autonomy
|
|
3826
|
+
unbindProactiveMessages = client.onWsEvent("proactive-message", (data) => {
|
|
3827
|
+
const parsed = parseProactiveMessageEvent(data);
|
|
3828
|
+
if (!parsed)
|
|
3829
|
+
return;
|
|
3830
|
+
const { conversationId: convId, message: msg } = parsed;
|
|
3831
|
+
if (convId === activeConversationIdRef.current) {
|
|
3832
|
+
// Active conversation — append in real-time (deduplicate by id)
|
|
3833
|
+
setConversationMessages((prev) => {
|
|
3834
|
+
if (prev.some((m) => m.id === msg.id))
|
|
3835
|
+
return prev;
|
|
3836
|
+
return [...prev, msg];
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
else {
|
|
3840
|
+
// Non-active — mark unread
|
|
3841
|
+
setUnreadConversations((prev) => new Set([...prev, convId]));
|
|
3842
|
+
}
|
|
3843
|
+
// Synthesize agent_event for non-retake sources (e.g. discord)
|
|
3844
|
+
// so they appear in the StreamView activity feed
|
|
3845
|
+
if (msg.source &&
|
|
3846
|
+
msg.source !== "retake" &&
|
|
3847
|
+
msg.source !== "client_chat" &&
|
|
3848
|
+
msg.role === "user") {
|
|
3849
|
+
appendAutonomousEvent({
|
|
3850
|
+
type: "agent_event",
|
|
3851
|
+
version: 1,
|
|
3852
|
+
eventId: `synth-${msg.id}`,
|
|
3853
|
+
ts: msg.timestamp,
|
|
3854
|
+
stream: "message",
|
|
3855
|
+
payload: {
|
|
3856
|
+
text: msg.text,
|
|
3857
|
+
from: msg.from,
|
|
3858
|
+
source: msg.source,
|
|
3859
|
+
direction: "inbound",
|
|
3860
|
+
channel: msg.source,
|
|
3861
|
+
},
|
|
3862
|
+
});
|
|
3863
|
+
}
|
|
3864
|
+
// Bump conversation to top of list
|
|
3865
|
+
setConversations((prev) => {
|
|
3866
|
+
const updated = prev.map((c) => c.id === convId
|
|
3867
|
+
? { ...c, updatedAt: new Date().toISOString() }
|
|
3868
|
+
: c);
|
|
3869
|
+
return updated.sort((a, b) => new Date(b.updatedAt).getTime() -
|
|
3870
|
+
new Date(a.updatedAt).getTime());
|
|
3871
|
+
});
|
|
3872
|
+
});
|
|
3873
|
+
// Handle conversation updates (e.g. title changes)
|
|
3874
|
+
client.onWsEvent("conversation-updated", (data) => {
|
|
3875
|
+
const conv = data.conversation;
|
|
3876
|
+
if (conv?.id) {
|
|
3877
|
+
setConversations((prev) => {
|
|
3878
|
+
const updated = prev.map((c) => (c.id === conv.id ? conv : c));
|
|
3879
|
+
return updated.sort((a, b) => new Date(b.updatedAt).getTime() -
|
|
3880
|
+
new Date(a.updatedAt).getTime());
|
|
3881
|
+
});
|
|
3882
|
+
}
|
|
3883
|
+
});
|
|
3884
|
+
// Handle PTY session events from SwarmCoordinator
|
|
3885
|
+
client.onWsEvent("pty-session-event", (data) => {
|
|
3886
|
+
const eventType = (data.eventType ?? data.type);
|
|
3887
|
+
const sessionId = data.sessionId;
|
|
3888
|
+
if (!sessionId)
|
|
3889
|
+
return;
|
|
3890
|
+
if (eventType === "task_registered") {
|
|
3891
|
+
const d = data.data;
|
|
3892
|
+
setPtySessions((prev) => [
|
|
3893
|
+
...prev.filter((s) => s.sessionId !== sessionId),
|
|
3894
|
+
{
|
|
3895
|
+
sessionId,
|
|
3896
|
+
agentType: d?.agentType ?? "claude",
|
|
3897
|
+
label: d?.label ?? sessionId,
|
|
3898
|
+
originalTask: d?.originalTask ?? "",
|
|
3899
|
+
workdir: d?.workdir ?? "",
|
|
3900
|
+
status: "active",
|
|
3901
|
+
decisionCount: 0,
|
|
3902
|
+
autoResolvedCount: 0,
|
|
3903
|
+
lastActivity: "Starting",
|
|
3904
|
+
},
|
|
3905
|
+
]);
|
|
3906
|
+
}
|
|
3907
|
+
else if (eventType === "task_complete" || eventType === "stopped") {
|
|
3908
|
+
setPtySessions((prev) => prev.filter((s) => s.sessionId !== sessionId));
|
|
3909
|
+
}
|
|
3910
|
+
else {
|
|
3911
|
+
// Status update — apply to known session, or full re-hydrate if unknown
|
|
3912
|
+
const applyUpdate = (prev) => {
|
|
3913
|
+
const known = prev.some((s) => s.sessionId === sessionId);
|
|
3914
|
+
if (!known)
|
|
3915
|
+
return prev; // will trigger hydration below
|
|
3916
|
+
if (eventType === "blocked" || eventType === "escalation") {
|
|
3917
|
+
const activity = eventType === "escalation"
|
|
3918
|
+
? "Escalated — needs attention"
|
|
3919
|
+
: "Waiting for input";
|
|
3920
|
+
return prev.map((s) => s.sessionId === sessionId
|
|
3921
|
+
? { ...s, status: "blocked", lastActivity: activity }
|
|
3922
|
+
: s);
|
|
3923
|
+
}
|
|
3924
|
+
if (eventType === "tool_running") {
|
|
3925
|
+
const d = data.data;
|
|
3926
|
+
const toolDesc = d?.description ??
|
|
3927
|
+
d?.toolName ??
|
|
3928
|
+
"external tool";
|
|
3929
|
+
return prev.map((s) => s.sessionId === sessionId
|
|
3930
|
+
? {
|
|
3931
|
+
...s,
|
|
3932
|
+
status: "tool_running",
|
|
3933
|
+
toolDescription: toolDesc,
|
|
3934
|
+
lastActivity: `Running ${toolDesc}`.slice(0, 60),
|
|
3935
|
+
}
|
|
3936
|
+
: s);
|
|
3937
|
+
}
|
|
3938
|
+
if (eventType === "blocked_auto_resolved") {
|
|
3939
|
+
const d = data.data;
|
|
3940
|
+
const prompt = d?.prompt ?? d?.reasoning ?? "";
|
|
3941
|
+
const excerpt = prompt
|
|
3942
|
+
? `Approved: ${prompt}`.slice(0, 60)
|
|
3943
|
+
: "Approved";
|
|
3944
|
+
return prev.map((s) => s.sessionId === sessionId
|
|
3945
|
+
? {
|
|
3946
|
+
...s,
|
|
3947
|
+
status: "active",
|
|
3948
|
+
toolDescription: undefined,
|
|
3949
|
+
lastActivity: excerpt,
|
|
3950
|
+
}
|
|
3951
|
+
: s);
|
|
3952
|
+
}
|
|
3953
|
+
// coordination_decision — emitted by swarm decision loop.
|
|
3954
|
+
// d.action values: "approve" | "respond" | "escalate" | "continue"
|
|
3955
|
+
if (eventType === "coordination_decision") {
|
|
3956
|
+
const d = data.data;
|
|
3957
|
+
const reasoning = d?.reasoning ?? d?.action ?? "";
|
|
3958
|
+
const wasEscalation = d?.action === "escalate";
|
|
3959
|
+
const excerpt = wasEscalation
|
|
3960
|
+
? `Escalated: ${reasoning}`.slice(0, 60)
|
|
3961
|
+
: reasoning
|
|
3962
|
+
? `Responded: ${reasoning}`.slice(0, 60)
|
|
3963
|
+
: "Responded";
|
|
3964
|
+
return prev.map((s) => s.sessionId === sessionId
|
|
3965
|
+
? {
|
|
3966
|
+
...s,
|
|
3967
|
+
status: "active",
|
|
3968
|
+
toolDescription: undefined,
|
|
3969
|
+
lastActivity: excerpt,
|
|
3970
|
+
}
|
|
3971
|
+
: s);
|
|
3972
|
+
}
|
|
3973
|
+
if (eventType === "ready") {
|
|
3974
|
+
return prev.map((s) => s.sessionId === sessionId
|
|
3975
|
+
? {
|
|
3976
|
+
...s,
|
|
3977
|
+
status: "active",
|
|
3978
|
+
toolDescription: undefined,
|
|
3979
|
+
lastActivity: "Running",
|
|
3980
|
+
}
|
|
3981
|
+
: s);
|
|
3982
|
+
}
|
|
3983
|
+
if (eventType === "error") {
|
|
3984
|
+
const d = data.data;
|
|
3985
|
+
const errMsg = d?.message ?? "Unknown error";
|
|
3986
|
+
return prev.map((s) => s.sessionId === sessionId
|
|
3987
|
+
? {
|
|
3988
|
+
...s,
|
|
3989
|
+
status: "error",
|
|
3990
|
+
lastActivity: `Error: ${errMsg}`.slice(0, 60),
|
|
3991
|
+
}
|
|
3992
|
+
: s);
|
|
3993
|
+
}
|
|
3994
|
+
return prev;
|
|
3995
|
+
};
|
|
3996
|
+
let needsHydrate = false;
|
|
3997
|
+
setPtySessions((prev) => {
|
|
3998
|
+
const next = applyUpdate(prev);
|
|
3999
|
+
if (next === prev && !prev.some((s) => s.sessionId === sessionId)) {
|
|
4000
|
+
// Unknown session — flag for re-hydration outside the updater
|
|
4001
|
+
needsHydrate = true;
|
|
4002
|
+
return prev;
|
|
4003
|
+
}
|
|
4004
|
+
return next;
|
|
4005
|
+
});
|
|
4006
|
+
if (needsHydrate) {
|
|
4007
|
+
// Re-hydrate from server to pick up missed registrations
|
|
4008
|
+
hydratePtySessions();
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
});
|
|
4012
|
+
// Load wallet addresses for header
|
|
4013
|
+
try {
|
|
4014
|
+
setWalletAddresses(await client.getWalletAddresses());
|
|
4015
|
+
}
|
|
4016
|
+
catch (err) {
|
|
4017
|
+
logStartupWarning("failed to load wallet addresses", err);
|
|
4018
|
+
}
|
|
4019
|
+
// Restore avatar selection from config (server-persisted under "ui")
|
|
4020
|
+
let resolvedIndex = loadAvatarIndex();
|
|
4021
|
+
try {
|
|
4022
|
+
const cfg = await client.getConfig();
|
|
4023
|
+
const ui = cfg.ui;
|
|
4024
|
+
if (ui?.avatarIndex != null) {
|
|
4025
|
+
resolvedIndex = normalizeAvatarIndex(Number(ui.avatarIndex));
|
|
4026
|
+
setSelectedVrmIndex(resolvedIndex);
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
catch (err) {
|
|
4030
|
+
logStartupWarning("failed to load config for avatar selection", err);
|
|
4031
|
+
}
|
|
4032
|
+
// If custom avatar selected, verify the file still exists on the server
|
|
4033
|
+
if (resolvedIndex === 0) {
|
|
4034
|
+
const hasVrm = await client.hasCustomVrm();
|
|
4035
|
+
if (hasVrm) {
|
|
4036
|
+
setCustomVrmUrl(resolveApiUrl(`/api/avatar/vrm?t=${Date.now()}`));
|
|
4037
|
+
}
|
|
4038
|
+
else {
|
|
4039
|
+
setSelectedVrmIndex(1);
|
|
4040
|
+
}
|
|
4041
|
+
// Restore custom background if one was uploaded
|
|
4042
|
+
const hasBg = await client.hasCustomBackground();
|
|
4043
|
+
if (hasBg) {
|
|
4044
|
+
setCustomBackgroundUrl(resolveApiUrl(`/api/avatar/background?t=${Date.now()}`));
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
// Cloud polling — always run the initial poll unconditionally so we can
|
|
4048
|
+
// discover a pre-existing API key / connection. If connected, start the
|
|
4049
|
+
// recurring interval too.
|
|
4050
|
+
pollCloudCredits().then((connected) => {
|
|
4051
|
+
if (connected) {
|
|
4052
|
+
elizaCloudPollInterval.current = window.setInterval(() => pollCloudCredits(), 60_000);
|
|
4053
|
+
}
|
|
4054
|
+
});
|
|
4055
|
+
// Load tab from URL — use hash in file:// mode (Electron packaged builds)
|
|
4056
|
+
const navPath = window.location.protocol === "file:"
|
|
4057
|
+
? window.location.hash.replace(/^#/, "") || "/"
|
|
4058
|
+
: window.location.pathname;
|
|
4059
|
+
const urlTab = tabFromPath(navPath);
|
|
4060
|
+
if (urlTab) {
|
|
4061
|
+
setTabRaw(urlTab);
|
|
4062
|
+
if (urlTab === "plugins" || urlTab === "connectors") {
|
|
4063
|
+
void loadPlugins();
|
|
4064
|
+
if (urlTab === "plugins") {
|
|
4065
|
+
void loadSkills();
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
if (urlTab === "settings") {
|
|
4069
|
+
void checkExtensionStatus();
|
|
4070
|
+
void loadWalletConfig();
|
|
4071
|
+
void loadCharacter();
|
|
4072
|
+
void loadUpdateStatus();
|
|
4073
|
+
void loadPlugins();
|
|
4074
|
+
}
|
|
4075
|
+
if (urlTab === "character") {
|
|
4076
|
+
void loadCharacter();
|
|
4077
|
+
}
|
|
4078
|
+
if (urlTab === "wallets") {
|
|
4079
|
+
void loadInventory();
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
};
|
|
4083
|
+
void initApp();
|
|
4084
|
+
// Navigation listener — use hashchange in file:// mode (Electron packaged builds)
|
|
4085
|
+
const isFileProtocol = window.location.protocol === "file:";
|
|
4086
|
+
const handleNavChange = () => {
|
|
4087
|
+
const navPath = isFileProtocol
|
|
4088
|
+
? window.location.hash.replace(/^#/, "") || "/"
|
|
4089
|
+
: window.location.pathname;
|
|
4090
|
+
const t = tabFromPath(navPath);
|
|
4091
|
+
if (t)
|
|
4092
|
+
setTabRaw(t);
|
|
4093
|
+
};
|
|
4094
|
+
const navEvent = isFileProtocol ? "hashchange" : "popstate";
|
|
4095
|
+
window.addEventListener(navEvent, handleNavChange);
|
|
4096
|
+
return () => {
|
|
4097
|
+
cancelled = true;
|
|
4098
|
+
window.removeEventListener(navEvent, handleNavChange);
|
|
4099
|
+
if (elizaCloudPollInterval.current) {
|
|
4100
|
+
clearInterval(elizaCloudPollInterval.current);
|
|
4101
|
+
elizaCloudPollInterval.current = null;
|
|
4102
|
+
}
|
|
4103
|
+
if (elizaCloudLoginPollTimer.current) {
|
|
4104
|
+
clearInterval(elizaCloudLoginPollTimer.current);
|
|
4105
|
+
elizaCloudLoginPollTimer.current = null;
|
|
4106
|
+
}
|
|
4107
|
+
unbindStatus?.();
|
|
4108
|
+
unbindAgentEvents?.();
|
|
4109
|
+
unbindHeartbeatEvents?.();
|
|
4110
|
+
unbindEmotes?.();
|
|
4111
|
+
unbindProactiveMessages?.();
|
|
4112
|
+
unbindWsReconnect?.();
|
|
4113
|
+
unbindSystemWarnings?.();
|
|
4114
|
+
unbindRestartRequired?.();
|
|
4115
|
+
if (handleVisibilityRef)
|
|
4116
|
+
document.removeEventListener("visibilitychange", handleVisibilityRef);
|
|
4117
|
+
client.disconnectWs();
|
|
4118
|
+
};
|
|
4119
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4120
|
+
}, [
|
|
4121
|
+
appendAutonomousEvent,
|
|
4122
|
+
checkExtensionStatus,
|
|
4123
|
+
fetchAutonomyReplay,
|
|
4124
|
+
hydrateInitialConversationState,
|
|
4125
|
+
loadCharacter,
|
|
4126
|
+
loadInventory,
|
|
4127
|
+
loadPlugins,
|
|
4128
|
+
loadSkills,
|
|
4129
|
+
loadUpdateStatus,
|
|
4130
|
+
loadWalletConfig,
|
|
4131
|
+
loadWorkbench, // Cloud polling
|
|
4132
|
+
pollCloudCredits,
|
|
4133
|
+
requestGreetingWhenRunning,
|
|
4134
|
+
setSelectedVrmIndex,
|
|
4135
|
+
startupRetryNonce,
|
|
4136
|
+
uiLanguage,
|
|
4137
|
+
]);
|
|
4138
|
+
// When agent transitions to "running", send a greeting if conversation is empty
|
|
4139
|
+
useEffect(() => {
|
|
4140
|
+
const current = agentStatus?.state ?? null;
|
|
4141
|
+
const prev = prevAgentStateRef.current;
|
|
4142
|
+
prevAgentStateRef.current = current;
|
|
4143
|
+
if (current === "running" && prev !== "running") {
|
|
4144
|
+
void loadWorkbench();
|
|
4145
|
+
// Agent just started — greet if conversation is empty.
|
|
4146
|
+
// The greetingFiredRef guard prevents double-greeting when both the
|
|
4147
|
+
// init mount effect and this state-transition effect race to fire.
|
|
4148
|
+
if (activeConversationId &&
|
|
4149
|
+
conversationMessages.length === 0 &&
|
|
4150
|
+
!chatSending &&
|
|
4151
|
+
!greetingFiredRef.current &&
|
|
4152
|
+
greetingInFlightConversationRef.current !== activeConversationId) {
|
|
4153
|
+
void fetchGreeting(activeConversationId);
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
}, [
|
|
4157
|
+
agentStatus?.state,
|
|
4158
|
+
loadWorkbench,
|
|
4159
|
+
activeConversationId,
|
|
4160
|
+
conversationMessages.length,
|
|
4161
|
+
chatSending,
|
|
4162
|
+
fetchGreeting,
|
|
4163
|
+
]);
|
|
4164
|
+
// ── Context value ──────────────────────────────────────────────────
|
|
4165
|
+
const t = useMemo(() => createTranslator(uiLanguage), [uiLanguage]);
|
|
4166
|
+
const value = {
|
|
4167
|
+
// Translations
|
|
4168
|
+
t,
|
|
4169
|
+
// State
|
|
4170
|
+
tab,
|
|
4171
|
+
uiShellMode,
|
|
4172
|
+
uiLanguage,
|
|
4173
|
+
uiTheme,
|
|
4174
|
+
connected,
|
|
4175
|
+
agentStatus,
|
|
4176
|
+
onboardingComplete,
|
|
4177
|
+
onboardingLoading,
|
|
4178
|
+
startupPhase,
|
|
4179
|
+
startupError,
|
|
4180
|
+
authRequired,
|
|
4181
|
+
actionNotice,
|
|
4182
|
+
lifecycleBusy,
|
|
4183
|
+
lifecycleAction,
|
|
4184
|
+
pendingRestart,
|
|
4185
|
+
pendingRestartReasons,
|
|
4186
|
+
restartBannerDismissed,
|
|
4187
|
+
backendConnection,
|
|
4188
|
+
backendDisconnectedBannerDismissed,
|
|
4189
|
+
pairingEnabled,
|
|
4190
|
+
pairingExpiresAt,
|
|
4191
|
+
pairingCodeInput,
|
|
4192
|
+
pairingError,
|
|
4193
|
+
pairingBusy,
|
|
4194
|
+
chatInput,
|
|
4195
|
+
chatSending,
|
|
4196
|
+
chatFirstTokenReceived,
|
|
4197
|
+
chatLastUsage,
|
|
4198
|
+
chatAvatarVisible,
|
|
4199
|
+
chatAgentVoiceMuted,
|
|
4200
|
+
chatMode,
|
|
4201
|
+
chatAvatarSpeaking,
|
|
4202
|
+
conversations,
|
|
4203
|
+
activeConversationId,
|
|
4204
|
+
companionMessageCutoffTs,
|
|
4205
|
+
conversationMessages,
|
|
4206
|
+
autonomousEvents,
|
|
4207
|
+
autonomousLatestEventId,
|
|
4208
|
+
autonomousRunHealthByRunId,
|
|
4209
|
+
ptySessions,
|
|
4210
|
+
unreadConversations,
|
|
4211
|
+
triggers,
|
|
4212
|
+
triggersLoading,
|
|
4213
|
+
triggersSaving,
|
|
4214
|
+
triggerRunsById,
|
|
4215
|
+
triggerHealth,
|
|
4216
|
+
triggerError,
|
|
4217
|
+
plugins,
|
|
4218
|
+
pluginFilter,
|
|
4219
|
+
pluginStatusFilter,
|
|
4220
|
+
pluginSearch,
|
|
4221
|
+
pluginSettingsOpen,
|
|
4222
|
+
pluginAdvancedOpen,
|
|
4223
|
+
pluginSaving,
|
|
4224
|
+
pluginSaveSuccess,
|
|
4225
|
+
skills,
|
|
4226
|
+
skillsSubTab,
|
|
4227
|
+
skillCreateFormOpen,
|
|
4228
|
+
skillCreateName,
|
|
4229
|
+
skillCreateDescription,
|
|
4230
|
+
skillCreating,
|
|
4231
|
+
skillReviewReport,
|
|
4232
|
+
skillReviewId,
|
|
4233
|
+
skillReviewLoading,
|
|
4234
|
+
skillToggleAction,
|
|
4235
|
+
skillsMarketplaceQuery,
|
|
4236
|
+
skillsMarketplaceResults,
|
|
4237
|
+
skillsMarketplaceError,
|
|
4238
|
+
skillsMarketplaceLoading,
|
|
4239
|
+
skillsMarketplaceAction,
|
|
4240
|
+
skillsMarketplaceManualGithubUrl,
|
|
4241
|
+
logs,
|
|
4242
|
+
logSources,
|
|
4243
|
+
logTags,
|
|
4244
|
+
logTagFilter,
|
|
4245
|
+
logLevelFilter,
|
|
4246
|
+
logSourceFilter,
|
|
4247
|
+
walletAddresses,
|
|
4248
|
+
walletConfig,
|
|
4249
|
+
walletBalances,
|
|
4250
|
+
walletNfts,
|
|
4251
|
+
walletLoading,
|
|
4252
|
+
walletNftsLoading,
|
|
4253
|
+
inventoryView,
|
|
4254
|
+
walletExportData,
|
|
4255
|
+
walletExportVisible,
|
|
4256
|
+
walletApiKeySaving,
|
|
4257
|
+
inventorySort,
|
|
4258
|
+
inventoryChainFocus,
|
|
4259
|
+
walletError,
|
|
4260
|
+
registryStatus,
|
|
4261
|
+
registryLoading,
|
|
4262
|
+
registryRegistering,
|
|
4263
|
+
registryError,
|
|
4264
|
+
dropStatus,
|
|
4265
|
+
dropLoading,
|
|
4266
|
+
mintInProgress,
|
|
4267
|
+
mintResult,
|
|
4268
|
+
mintError,
|
|
4269
|
+
mintShiny,
|
|
4270
|
+
whitelistStatus,
|
|
4271
|
+
whitelistLoading,
|
|
4272
|
+
twitterVerifyMessage,
|
|
4273
|
+
twitterVerifyUrl,
|
|
4274
|
+
twitterVerifying,
|
|
4275
|
+
characterData,
|
|
4276
|
+
characterLoading,
|
|
4277
|
+
characterSaving,
|
|
4278
|
+
characterSaveSuccess,
|
|
4279
|
+
characterSaveError,
|
|
4280
|
+
characterDraft,
|
|
4281
|
+
selectedVrmIndex,
|
|
4282
|
+
customVrmUrl,
|
|
4283
|
+
customBackgroundUrl,
|
|
4284
|
+
elizaCloudEnabled,
|
|
4285
|
+
elizaCloudConnected,
|
|
4286
|
+
elizaCloudCredits,
|
|
4287
|
+
elizaCloudCreditsLow,
|
|
4288
|
+
elizaCloudCreditsCritical,
|
|
4289
|
+
elizaCloudTopUpUrl,
|
|
4290
|
+
elizaCloudUserId,
|
|
4291
|
+
cloudDashboardView,
|
|
4292
|
+
elizaCloudLoginBusy,
|
|
4293
|
+
elizaCloudLoginError,
|
|
4294
|
+
elizaCloudDisconnecting,
|
|
4295
|
+
updateStatus,
|
|
4296
|
+
updateLoading,
|
|
4297
|
+
updateChannelSaving,
|
|
4298
|
+
extensionStatus,
|
|
4299
|
+
extensionChecking,
|
|
4300
|
+
storePlugins,
|
|
4301
|
+
storeSearch,
|
|
4302
|
+
storeFilter,
|
|
4303
|
+
storeLoading,
|
|
4304
|
+
storeInstalling,
|
|
4305
|
+
storeUninstalling,
|
|
4306
|
+
storeError,
|
|
4307
|
+
storeDetailPlugin,
|
|
4308
|
+
storeSubTab,
|
|
4309
|
+
catalogSkills,
|
|
4310
|
+
catalogTotal,
|
|
4311
|
+
catalogPage,
|
|
4312
|
+
catalogTotalPages,
|
|
4313
|
+
catalogSort,
|
|
4314
|
+
catalogSearch,
|
|
4315
|
+
catalogLoading,
|
|
4316
|
+
catalogError,
|
|
4317
|
+
catalogDetailSkill,
|
|
4318
|
+
catalogInstalling,
|
|
4319
|
+
catalogUninstalling,
|
|
4320
|
+
workbenchLoading,
|
|
4321
|
+
workbench,
|
|
4322
|
+
workbenchTasksAvailable,
|
|
4323
|
+
workbenchTriggersAvailable,
|
|
4324
|
+
workbenchTodosAvailable,
|
|
4325
|
+
exportBusy,
|
|
4326
|
+
exportPassword,
|
|
4327
|
+
exportIncludeLogs,
|
|
4328
|
+
exportError,
|
|
4329
|
+
exportSuccess,
|
|
4330
|
+
importBusy,
|
|
4331
|
+
importPassword,
|
|
4332
|
+
importFile,
|
|
4333
|
+
importError,
|
|
4334
|
+
importSuccess,
|
|
4335
|
+
onboardingStep,
|
|
4336
|
+
onboardingOptions,
|
|
4337
|
+
onboardingName,
|
|
4338
|
+
onboardingOwnerName,
|
|
4339
|
+
onboardingStyle,
|
|
4340
|
+
onboardingRunMode,
|
|
4341
|
+
onboardingCloudProvider,
|
|
4342
|
+
onboardingSmallModel,
|
|
4343
|
+
onboardingLargeModel,
|
|
4344
|
+
onboardingProvider,
|
|
4345
|
+
onboardingApiKey,
|
|
4346
|
+
onboardingRemoteApiBase,
|
|
4347
|
+
onboardingRemoteToken,
|
|
4348
|
+
onboardingRemoteConnecting,
|
|
4349
|
+
onboardingRemoteError,
|
|
4350
|
+
onboardingRemoteConnected,
|
|
4351
|
+
onboardingOpenRouterModel,
|
|
4352
|
+
onboardingPrimaryModel,
|
|
4353
|
+
onboardingTelegramToken,
|
|
4354
|
+
onboardingDiscordToken,
|
|
4355
|
+
onboardingWhatsAppSessionPath,
|
|
4356
|
+
onboardingTwilioAccountSid,
|
|
4357
|
+
onboardingTwilioAuthToken,
|
|
4358
|
+
onboardingTwilioPhoneNumber,
|
|
4359
|
+
onboardingBlooioApiKey,
|
|
4360
|
+
onboardingBlooioPhoneNumber,
|
|
4361
|
+
onboardingGithubToken,
|
|
4362
|
+
onboardingSubscriptionTab,
|
|
4363
|
+
onboardingElizaCloudTab,
|
|
4364
|
+
onboardingSelectedChains,
|
|
4365
|
+
onboardingRpcSelections,
|
|
4366
|
+
onboardingRpcKeys,
|
|
4367
|
+
onboardingAvatar,
|
|
4368
|
+
onboardingRestarting,
|
|
4369
|
+
commandPaletteOpen,
|
|
4370
|
+
commandQuery,
|
|
4371
|
+
commandActiveIndex,
|
|
4372
|
+
closeCommandPalette,
|
|
4373
|
+
emotePickerOpen,
|
|
4374
|
+
mcpConfiguredServers,
|
|
4375
|
+
mcpServerStatuses,
|
|
4376
|
+
mcpMarketplaceQuery,
|
|
4377
|
+
mcpMarketplaceResults,
|
|
4378
|
+
mcpMarketplaceLoading,
|
|
4379
|
+
mcpAction,
|
|
4380
|
+
mcpAddingServer,
|
|
4381
|
+
mcpAddingResult,
|
|
4382
|
+
mcpEnvInputs,
|
|
4383
|
+
mcpHeaderInputs,
|
|
4384
|
+
droppedFiles,
|
|
4385
|
+
shareIngestNotice,
|
|
4386
|
+
chatPendingImages,
|
|
4387
|
+
activeGameApp,
|
|
4388
|
+
activeGameDisplayName,
|
|
4389
|
+
activeGameViewerUrl,
|
|
4390
|
+
activeGameSandbox,
|
|
4391
|
+
activeGamePostMessageAuth,
|
|
4392
|
+
gameOverlayEnabled,
|
|
4393
|
+
appsSubTab,
|
|
4394
|
+
agentSubTab,
|
|
4395
|
+
pluginsSubTab,
|
|
4396
|
+
databaseSubTab,
|
|
4397
|
+
configRaw,
|
|
4398
|
+
configText,
|
|
4399
|
+
activeGamePostMessagePayload,
|
|
4400
|
+
// Actions
|
|
4401
|
+
setTab,
|
|
4402
|
+
setUiShellMode,
|
|
4403
|
+
switchUiShellMode,
|
|
4404
|
+
switchShellView,
|
|
4405
|
+
setUiLanguage,
|
|
4406
|
+
setUiTheme,
|
|
4407
|
+
handleStart,
|
|
4408
|
+
handleStop,
|
|
4409
|
+
handleRestart,
|
|
4410
|
+
handleReset,
|
|
4411
|
+
retryStartup,
|
|
4412
|
+
dismissRestartBanner,
|
|
4413
|
+
triggerRestart,
|
|
4414
|
+
dismissBackendDisconnectedBanner,
|
|
4415
|
+
retryBackendConnection,
|
|
4416
|
+
restartBackend,
|
|
4417
|
+
systemWarnings,
|
|
4418
|
+
dismissSystemWarning,
|
|
4419
|
+
handleChatSend,
|
|
4420
|
+
handleChatStop,
|
|
4421
|
+
handleChatRetry,
|
|
4422
|
+
handleChatEdit,
|
|
4423
|
+
handleChatClear,
|
|
4424
|
+
handleNewConversation,
|
|
4425
|
+
setChatPendingImages,
|
|
4426
|
+
handleSelectConversation,
|
|
4427
|
+
handleDeleteConversation,
|
|
4428
|
+
handleRenameConversation,
|
|
4429
|
+
sendActionMessage,
|
|
4430
|
+
loadTriggers,
|
|
4431
|
+
createTrigger,
|
|
4432
|
+
updateTrigger,
|
|
4433
|
+
deleteTrigger,
|
|
4434
|
+
runTriggerNow,
|
|
4435
|
+
loadTriggerRuns,
|
|
4436
|
+
loadTriggerHealth,
|
|
4437
|
+
handlePairingSubmit,
|
|
4438
|
+
loadPlugins,
|
|
4439
|
+
handlePluginToggle,
|
|
4440
|
+
handlePluginConfigSave,
|
|
4441
|
+
loadSkills,
|
|
4442
|
+
refreshSkills,
|
|
4443
|
+
handleSkillToggle,
|
|
4444
|
+
handleCreateSkill,
|
|
4445
|
+
handleOpenSkill,
|
|
4446
|
+
handleDeleteSkill,
|
|
4447
|
+
handleReviewSkill,
|
|
4448
|
+
handleAcknowledgeSkill,
|
|
4449
|
+
searchSkillsMarketplace,
|
|
4450
|
+
installSkillFromMarketplace,
|
|
4451
|
+
uninstallMarketplaceSkill,
|
|
4452
|
+
installSkillFromGithubUrl,
|
|
4453
|
+
loadLogs,
|
|
4454
|
+
loadInventory,
|
|
4455
|
+
loadBalances,
|
|
4456
|
+
loadNfts,
|
|
4457
|
+
executeBscTrade,
|
|
4458
|
+
executeBscTransfer,
|
|
4459
|
+
getBscTradePreflight,
|
|
4460
|
+
getBscTradeQuote,
|
|
4461
|
+
getBscTradeTxStatus,
|
|
4462
|
+
loadWalletTradingProfile,
|
|
4463
|
+
handleWalletApiKeySave,
|
|
4464
|
+
handleExportKeys,
|
|
4465
|
+
loadRegistryStatus,
|
|
4466
|
+
registerOnChain,
|
|
4467
|
+
syncRegistryProfile,
|
|
4468
|
+
loadDropStatus,
|
|
4469
|
+
mintFromDrop,
|
|
4470
|
+
loadWhitelistStatus,
|
|
4471
|
+
loadCharacter,
|
|
4472
|
+
handleSaveCharacter,
|
|
4473
|
+
handleCharacterFieldInput,
|
|
4474
|
+
handleCharacterArrayInput,
|
|
4475
|
+
handleCharacterStyleInput,
|
|
4476
|
+
handleCharacterMessageExamplesInput,
|
|
4477
|
+
handleOnboardingNext,
|
|
4478
|
+
handleOnboardingBack,
|
|
4479
|
+
handleOnboardingRemoteConnect,
|
|
4480
|
+
handleOnboardingUseLocalBackend,
|
|
4481
|
+
handleCloudLogin,
|
|
4482
|
+
handleCloudDisconnect,
|
|
4483
|
+
loadUpdateStatus,
|
|
4484
|
+
handleChannelChange,
|
|
4485
|
+
checkExtensionStatus,
|
|
4486
|
+
openEmotePicker,
|
|
4487
|
+
closeEmotePicker,
|
|
4488
|
+
loadWorkbench,
|
|
4489
|
+
handleAgentExport,
|
|
4490
|
+
handleAgentImport,
|
|
4491
|
+
setActionNotice,
|
|
4492
|
+
setState,
|
|
4493
|
+
copyToClipboard,
|
|
4494
|
+
};
|
|
4495
|
+
return (_jsxs(AppContext.Provider, { value: value, children: [children, _jsx(ConfirmModal, { ...modalProps }), _jsx(PromptModal, { ...promptModalProps })] }));
|
|
4496
|
+
}
|