neoagent 2.3.1-beta.2 → 2.3.1-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/.env.example +39 -0
  2. package/README.md +2 -0
  3. package/docs/capabilities.md +2 -2
  4. package/docs/configuration.md +13 -5
  5. package/docs/integrations.md +4 -1
  6. package/flutter_app/.metadata +42 -0
  7. package/flutter_app/README.md +21 -0
  8. package/flutter_app/analysis_options.yaml +32 -0
  9. package/flutter_app/android/app/build.gradle.kts +109 -0
  10. package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
  11. package/flutter_app/android/app/src/main/AndroidManifest.xml +147 -0
  12. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +747 -0
  13. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthConnectGateway.kt +280 -0
  14. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncNotifications.kt +113 -0
  15. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncPayload.kt +57 -0
  16. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncScheduler.kt +78 -0
  17. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncWorker.kt +253 -0
  18. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/PermissionsRationaleActivity.kt +46 -0
  19. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +21 -0
  20. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +586 -0
  21. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +78 -0
  22. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +104 -0
  23. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiHomeWidgetProvider.kt +457 -0
  24. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiWidgetStore.kt +194 -0
  25. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/VoiceLaunchWidgetProvider.kt +67 -0
  26. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetConfigActivity.kt +228 -0
  27. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncScheduler.kt +72 -0
  28. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncWorker.kt +186 -0
  29. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetTaskRunWorker.kt +210 -0
  30. package/flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  31. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_bg.xml +11 -0
  32. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_task_bg.xml +8 -0
  33. package/flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  34. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget.xml +138 -0
  35. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget_task_row.xml +52 -0
  36. package/flutter_app/android/app/src/main/res/layout/neoagent_voice_widget.xml +49 -0
  37. package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  38. package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  39. package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  40. package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  41. package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  42. package/flutter_app/android/app/src/main/res/values/strings.xml +12 -0
  43. package/flutter_app/android/app/src/main/res/values/styles.xml +18 -0
  44. package/flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
  45. package/flutter_app/android/app/src/main/res/xml/file_paths.xml +6 -0
  46. package/flutter_app/android/app/src/main/res/xml/neoagent_ai_widget_info.xml +12 -0
  47. package/flutter_app/android/app/src/main/res/xml/neoagent_voice_widget_info.xml +12 -0
  48. package/flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
  49. package/flutter_app/android/build.gradle.kts +24 -0
  50. package/flutter_app/android/ci-release.keystore +0 -0
  51. package/flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  52. package/flutter_app/android/gradle.properties +3 -0
  53. package/flutter_app/android/key.properties +4 -0
  54. package/flutter_app/android/settings.gradle.kts +26 -0
  55. package/flutter_app/assets/branding/app_icon_1024.png +0 -0
  56. package/flutter_app/assets/branding/app_icon_128.png +0 -0
  57. package/flutter_app/assets/branding/app_icon_192.png +0 -0
  58. package/flutter_app/assets/branding/app_icon_256.png +0 -0
  59. package/flutter_app/assets/branding/app_icon_32.png +0 -0
  60. package/flutter_app/assets/branding/app_icon_512.png +0 -0
  61. package/flutter_app/assets/branding/app_icon_64.png +0 -0
  62. package/flutter_app/assets/branding/tray_icon_template.png +0 -0
  63. package/flutter_app/lib/features/location/location_service.dart +119 -0
  64. package/flutter_app/lib/features/notifications/notification_interceptor.dart +97 -0
  65. package/flutter_app/lib/main.dart +23057 -0
  66. package/flutter_app/lib/main_app_shell.dart +1682 -0
  67. package/flutter_app/lib/main_integrations.dart +931 -0
  68. package/flutter_app/lib/main_launcher.dart +959 -0
  69. package/flutter_app/lib/main_launcher_entry.dart +5 -0
  70. package/flutter_app/lib/main_models.dart +3473 -0
  71. package/flutter_app/lib/main_shared.dart +2861 -0
  72. package/flutter_app/lib/main_theme.dart +204 -0
  73. package/flutter_app/lib/main_voice_assistant.dart +831 -0
  74. package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
  75. package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
  76. package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
  77. package/flutter_app/lib/src/android_app_installer.dart +22 -0
  78. package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
  79. package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
  80. package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
  81. package/flutter_app/lib/src/app_launch_bridge.dart +29 -0
  82. package/flutter_app/lib/src/app_release_updater.dart +511 -0
  83. package/flutter_app/lib/src/backend_client.dart +1833 -0
  84. package/flutter_app/lib/src/desktop_companion.dart +2 -0
  85. package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
  86. package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
  87. package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
  88. package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
  89. package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
  90. package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
  91. package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
  92. package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
  93. package/flutter_app/lib/src/health_bridge.dart +136 -0
  94. package/flutter_app/lib/src/live_voice_capture.dart +85 -0
  95. package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
  96. package/flutter_app/lib/src/network/app_http_client.dart +53 -0
  97. package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
  98. package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
  99. package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
  100. package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
  101. package/flutter_app/lib/src/oauth_launcher.dart +33 -0
  102. package/flutter_app/lib/src/oauth_launcher_io.dart +77 -0
  103. package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
  104. package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
  105. package/flutter_app/lib/src/recording_bridge.dart +232 -0
  106. package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
  107. package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
  108. package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
  109. package/flutter_app/lib/src/recording_payloads.dart +86 -0
  110. package/flutter_app/lib/src/theme/palette.dart +81 -0
  111. package/flutter_app/lib/src/widget_bridge.dart +49 -0
  112. package/flutter_app/linux/CMakeLists.txt +128 -0
  113. package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
  114. package/flutter_app/linux/flutter/generated_plugin_registrant.cc +43 -0
  115. package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
  116. package/flutter_app/linux/flutter/generated_plugins.cmake +31 -0
  117. package/flutter_app/linux/runner/CMakeLists.txt +26 -0
  118. package/flutter_app/linux/runner/main.cc +6 -0
  119. package/flutter_app/linux/runner/my_application.cc +144 -0
  120. package/flutter_app/linux/runner/my_application.h +18 -0
  121. package/flutter_app/linux/runner/resources/app_icon.png +0 -0
  122. package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  123. package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  124. package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +40 -0
  125. package/flutter_app/macos/Podfile +42 -0
  126. package/flutter_app/macos/Podfile.lock +87 -0
  127. package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
  128. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  129. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  130. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  131. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  132. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  133. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  134. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  135. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  136. package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
  137. package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  138. package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
  139. package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
  140. package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  141. package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
  142. package/flutter_app/macos/Runner/Info.plist +36 -0
  143. package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
  144. package/flutter_app/macos/Runner/Release.entitlements +12 -0
  145. package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
  146. package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  147. package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
  148. package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  149. package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  150. package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
  151. package/flutter_app/patch_strings.py +12 -0
  152. package/flutter_app/pubspec.lock +1088 -0
  153. package/flutter_app/pubspec.yaml +53 -0
  154. package/flutter_app/test/messaging_access_summary_test.dart +22 -0
  155. package/flutter_app/test/recording_payloads_test.dart +53 -0
  156. package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
  157. package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
  158. package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
  159. package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
  160. package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
  161. package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
  162. package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
  163. package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
  164. package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
  165. package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
  166. package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
  167. package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
  168. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
  169. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
  170. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
  171. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
  172. package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
  173. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
  174. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
  175. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
  176. package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
  177. package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
  178. package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
  179. package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
  180. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
  181. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
  182. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
  183. package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
  184. package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
  185. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
  186. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
  187. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
  188. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
  189. package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
  190. package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
  191. package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
  192. package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
  193. package/flutter_app/tool/generate_desktop_branding.py +219 -0
  194. package/flutter_app/web/favicon.png +0 -0
  195. package/flutter_app/web/favicon.svg +12 -0
  196. package/flutter_app/web/icons/Icon-192.png +0 -0
  197. package/flutter_app/web/icons/Icon-512.png +0 -0
  198. package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
  199. package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
  200. package/flutter_app/web/index.html +39 -0
  201. package/flutter_app/web/manifest.json +35 -0
  202. package/flutter_app/windows/CMakeLists.txt +108 -0
  203. package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
  204. package/flutter_app/windows/flutter/generated_plugin_registrant.cc +47 -0
  205. package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
  206. package/flutter_app/windows/flutter/generated_plugins.cmake +35 -0
  207. package/flutter_app/windows/runner/CMakeLists.txt +41 -0
  208. package/flutter_app/windows/runner/Runner.rc +121 -0
  209. package/flutter_app/windows/runner/flutter_window.cpp +533 -0
  210. package/flutter_app/windows/runner/flutter_window.h +37 -0
  211. package/flutter_app/windows/runner/main.cpp +53 -0
  212. package/flutter_app/windows/runner/resource.h +16 -0
  213. package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
  214. package/flutter_app/windows/runner/runner.exe.manifest +14 -0
  215. package/flutter_app/windows/runner/utils.cpp +65 -0
  216. package/flutter_app/windows/runner/utils.h +19 -0
  217. package/flutter_app/windows/runner/win32_window.cpp +299 -0
  218. package/flutter_app/windows/runner/win32_window.h +102 -0
  219. package/lib/manager.js +231 -7
  220. package/package.json +3 -1
  221. package/server/db/database.js +68 -0
  222. package/server/http/middleware.js +50 -0
  223. package/server/http/routes.js +3 -1
  224. package/server/index.js +1 -0
  225. package/server/public/.last_build_id +1 -1
  226. package/server/public/assets/NOTICES +61 -0
  227. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  228. package/server/public/flutter_bootstrap.js +1 -1
  229. package/server/public/main.dart.js +65262 -64422
  230. package/server/routes/integrations.js +86 -0
  231. package/server/routes/memory.js +11 -2
  232. package/server/routes/screenHistory.js +46 -0
  233. package/server/routes/triggers.js +81 -0
  234. package/server/services/ai/models.js +30 -0
  235. package/server/services/ai/providers/githubCopilot.js +97 -0
  236. package/server/services/ai/providers/openai.js +2 -1
  237. package/server/services/ai/providers/openaiCodex.js +31 -0
  238. package/server/services/ai/settings.js +20 -0
  239. package/server/services/ai/systemPrompt.js +1 -1
  240. package/server/services/ai/tools.js +35 -6
  241. package/server/services/browser/controller.js +47 -3
  242. package/server/services/desktop/screenRecorder.js +172 -0
  243. package/server/services/integrations/env.js +5 -0
  244. package/server/services/integrations/github/common.js +106 -0
  245. package/server/services/integrations/github/provider.js +499 -0
  246. package/server/services/integrations/github/repos.js +1124 -0
  247. package/server/services/integrations/home_assistant/provider.js +306 -26
  248. package/server/services/integrations/manager.js +63 -7
  249. package/server/services/integrations/oauth_provider.js +13 -6
  250. package/server/services/integrations/provider_config_store.js +76 -0
  251. package/server/services/integrations/registry.js +4 -0
  252. package/server/services/integrations/trello/provider.js +744 -0
  253. package/server/services/integrations/whatsapp/provider.js +6 -2
  254. package/server/services/manager.js +22 -0
  255. package/server/services/memory/manager.js +39 -2
  256. package/server/services/skills/base_catalog.js +33 -0
  257. package/server/services/tasks/adapters/index.js +1 -0
  258. package/server/services/tasks/adapters/manual.js +12 -0
  259. package/server/services/tasks/runtime.js +1 -1
  260. package/server/services/voice/openaiClient.js +4 -1
  261. package/server/services/voice/providers.js +2 -1
  262. package/server/services/widgets/service.js +49 -4
  263. package/server/utils/local_secrets.js +56 -0
  264. package/server/utils/logger.js +37 -9
@@ -545,7 +545,7 @@ class WhatsAppPersonalProvider extends EventEmitter {
545
545
  chatCount: client.chats.size,
546
546
  cachedMessageChats: client.messages.size,
547
547
  syncNote:
548
- 'Read tools only expose chats and messages synchronized through this personal integration session.',
548
+ 'This account is linked and connected. Read tools expose only chats and messages currently available in this session cache.',
549
549
  },
550
550
  };
551
551
  case 'whatsapp_personal_list_chats': {
@@ -557,10 +557,14 @@ class WhatsAppPersonalProvider extends EventEmitter {
557
557
  .slice(0, limit);
558
558
  return {
559
559
  result: {
560
+ account: client.accountEmail || connection.account_email || null,
561
+ connected: client.status === 'connected',
560
562
  chats,
561
563
  count: chats.length,
562
564
  syncNote:
563
- 'Only chats synchronized after linking this personal integration are available here.',
565
+ chats.length > 0
566
+ ? 'Chats shown here come from the current personal WhatsApp cache.'
567
+ : 'The personal WhatsApp account is connected, but no chats are cached yet. The cache populates as history/events are synchronized.',
564
568
  },
565
569
  };
566
570
  }
@@ -24,6 +24,7 @@ const { RuntimeManager } = require('./runtime/manager');
24
24
  const { BrowserExtensionRegistry } = require('./browser/extension/registry');
25
25
  const { DesktopCompanionRegistry } = require('./desktop/registry');
26
26
  const { DesktopProvider } = require('./desktop/provider');
27
+ const { ScreenRecorder } = require('./desktop/screenRecorder');
27
28
  const { assertRuntimeValidation, getRuntimeValidation } = require('./runtime/validation');
28
29
  const {
29
30
  getErrorMessage,
@@ -428,6 +429,17 @@ function createWidgetService(app) {
428
429
  return widgetService;
429
430
  }
430
431
 
432
+ function createScreenRecorder(app) {
433
+ const screenRecorder = registerLocal(
434
+ app,
435
+ 'screenRecorder',
436
+ new ScreenRecorder(),
437
+ );
438
+ screenRecorder.start();
439
+ logServiceReady('Screen recorder started');
440
+ return screenRecorder;
441
+ }
442
+
431
443
  function restoreMessagingConnections(messagingManager) {
432
444
  void runBackgroundTask('[Messaging] Restore error:', () =>
433
445
  messagingManager.restoreConnections(),
@@ -513,6 +525,7 @@ async function startServices(app, io) {
513
525
  const messagingManager = createMessagingManager(app, io, agentEngine);
514
526
  const recordingManager = createRecordingManager(app, io);
515
527
  createWidgetService(app);
528
+ createScreenRecorder(app);
516
529
 
517
530
  restoreMessagingConnections(messagingManager);
518
531
  restoreMcpClients(mcpClient);
@@ -639,6 +652,15 @@ async function stopServices(app) {
639
652
  }
640
653
  }
641
654
 
655
+ if (app.locals.screenRecorder) {
656
+ try {
657
+ app.locals.screenRecorder.stop();
658
+ logServiceReady('Screen recorder stopped');
659
+ } catch (err) {
660
+ console.error('[ScreenRecorder] Stop error:', getErrorMessage(err));
661
+ }
662
+ }
663
+
642
664
  if (app.locals.browserExtensionRegistry) {
643
665
  try {
644
666
  app.locals.browserExtensionRegistry.closeAll();
@@ -12,6 +12,11 @@ const {
12
12
  const { getMemoryStorageDecision } = require('./policy');
13
13
  const { AGENT_DATA_DIR } = require('../../../runtime/paths');
14
14
  const { isMainAgent, resolveAgentId } = require('../agents/manager');
15
+ const {
16
+ decryptLocalValue,
17
+ encryptLocalValue,
18
+ isLocalEncryptedValue,
19
+ } = require('../../utils/local_secrets');
15
20
 
16
21
  async function getActiveProvider(userId, agentId = null) {
17
22
  try {
@@ -516,11 +521,43 @@ class MemoryManager {
516
521
  return {};
517
522
  }
518
523
  }
519
- try { return JSON.parse(fs.readFileSync(filePath, 'utf-8')); } catch { return {}; }
524
+ try {
525
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
526
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
527
+ return {};
528
+ }
529
+
530
+ let shouldMigrate = false;
531
+ const normalized = {};
532
+
533
+ for (const [service, rawValue] of Object.entries(parsed)) {
534
+ const value = String(rawValue || '');
535
+ if (!value) {
536
+ normalized[service] = '';
537
+ continue;
538
+ }
539
+
540
+ if (!isLocalEncryptedValue(value)) shouldMigrate = true;
541
+ normalized[service] = decryptLocalValue(value);
542
+ }
543
+
544
+ if (shouldMigrate) {
545
+ this.writeApiKeys(normalized, userId);
546
+ }
547
+
548
+ return normalized;
549
+ } catch {
550
+ return {};
551
+ }
520
552
  }
521
553
 
522
554
  writeApiKeys(keys, userId = null) {
523
- fs.writeFileSync(this._userApiKeysPath(userId), JSON.stringify(keys, null, 2), 'utf-8');
555
+ const encrypted = {};
556
+ for (const [service, rawValue] of Object.entries(keys || {})) {
557
+ const value = String(rawValue || '');
558
+ encrypted[service] = value ? encryptLocalValue(value) : '';
559
+ }
560
+ fs.writeFileSync(this._userApiKeysPath(userId), JSON.stringify(encrypted, null, 2), 'utf-8');
524
561
  }
525
562
 
526
563
  setApiKey(service, key, userId = null) {
@@ -1161,6 +1161,39 @@ Auto-orients and arranges the STL, applies settings from JSON files, slices all
1161
1161
  3. Build the command from the flags above
1162
1162
  4. Run it and check for errors in the output (debug level 2 is a good default)
1163
1163
  5. Report the output file location and any warnings`
1164
+ },
1165
+
1166
+ {
1167
+ id: 'package-tracker',
1168
+ name: 'Package Tracker',
1169
+ description: 'Track packages by automatically detecting the carrier and fetching the tracking link.',
1170
+ category: 'info',
1171
+ icon: '📦',
1172
+ content: `---
1173
+ name: package-tracker
1174
+ description: Track packages by automatically detecting the carrier and fetching the tracking link.
1175
+ category: info
1176
+ icon: 📦
1177
+ enabled: true
1178
+ ---
1179
+
1180
+ You are a package tracking assistant. When the user gives you a tracking number, you should:
1181
+
1182
+ 1. Identify the carrier using common regex patterns:
1183
+ - UPS: \`\\b(1Z[0-9A-Z]{16})\\b\`
1184
+ - FedEx: \`\\b([0-9]{12,15})\\b\` (usually 12 or 15 digits)
1185
+ - USPS: \`\\b(94[0-9]{20})\\b\` or \`\\b([A-Z]{2}[0-9]{9}US)\\b\`
1186
+ - DHL: \`\\b([0-9]{10})\\b\` or \`\\b([0-9]{20})\\b\`
1187
+
1188
+ 2. Generate the direct tracking link for the user:
1189
+ - UPS: \`https://www.ups.com/track?tracknum={number}\`
1190
+ - FedEx: \`https://www.fedex.com/fedextrack/?trknbr={number}\`
1191
+ - USPS: \`https://tools.usps.com/go/TrackConfirmAction?tLabels={number}\`
1192
+ - DHL: \`https://www.dhl.com/global-en/home/tracking/tracking-express.html?submit=1&tracking-id={number}\`
1193
+
1194
+ 3. If the carrier is not obvious, use \`web_search\` with the query "track package {number}" to identify the carrier.
1195
+ 4. Present the carrier and the direct tracking link to the user clearly.
1196
+ 5. Optionally use \`browser_navigate\` or \`http_request\` to fetch the current status from the tracking link, but note that many carriers block automated scraping. The direct link is the most important output.`
1164
1197
  }
1165
1198
  ];
1166
1199
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  module.exports = [
4
4
  require('./schedule'),
5
+ require('./manual'),
5
6
  require('./gmail_message_received'),
6
7
  require('./outlook_email_received'),
7
8
  require('./slack_message_received'),
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ type: 'manual',
5
+ label: 'Manual Trigger',
6
+ async validateConfig() {
7
+ return {};
8
+ },
9
+ summarize() {
10
+ return 'Manual run only';
11
+ },
12
+ };
@@ -66,7 +66,7 @@ class TaskRuntime {
66
66
  label: adapter.label,
67
67
  providerKey: adapter.providerKey || null,
68
68
  appKey: adapter.appKey || null,
69
- available: adapter.type === 'schedule'
69
+ available: adapter.type === 'schedule' || adapter.type === 'manual'
70
70
  ? true
71
71
  : this._hasConnectedApp(userId, agentId, adapter.providerKey, adapter.appKey),
72
72
  }));
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { OpenAI } = require('openai');
6
6
  const { AGENT_DATA_DIR } = require('../../../runtime/paths');
7
+ const { decryptLocalValue } = require('../../utils/local_secrets');
7
8
 
8
9
  let cachedClient = null;
9
10
 
@@ -15,7 +16,9 @@ function resolveOpenAiApiKey() {
15
16
  try {
16
17
  const keysPath = path.join(AGENT_DATA_DIR, 'API_KEYS.json');
17
18
  const keys = JSON.parse(fs.readFileSync(keysPath, 'utf8'));
18
- const candidate = keys.OPENAI_API_KEY || keys.openai_api_key || keys.openai;
19
+ const candidate = decryptLocalValue(keys.OPENAI_API_KEY)
20
+ || decryptLocalValue(keys.openai_api_key)
21
+ || decryptLocalValue(keys.openai);
19
22
  return typeof candidate === 'string' && candidate.trim() ? candidate.trim() : '';
20
23
  } catch {
21
24
  return '';
@@ -6,6 +6,7 @@ const { AGENT_DATA_DIR } = require('../../../runtime/paths');
6
6
  const { getOpenAiClient } = require('./openaiClient');
7
7
  const { synthesizeSpeechBuffer } = require('./openaiSpeech');
8
8
  const { transcribeChunkWithDeepgram } = require('../recordings/deepgram');
9
+ const { decryptLocalValue } = require('../../utils/local_secrets');
9
10
 
10
11
  const DEFAULT_STT_PROVIDER = 'openai';
11
12
  const DEFAULT_TTS_PROVIDER = 'openai';
@@ -94,7 +95,7 @@ function resolveApiKey(candidates = []) {
94
95
  const snake = lower.replace(/[^a-z0-9]+/g, '_');
95
96
  const variants = [key, lower, snake];
96
97
  for (const variant of variants) {
97
- const value = keys[variant];
98
+ const value = decryptLocalValue(keys[variant]);
98
99
  if (typeof value === 'string' && value.trim()) {
99
100
  return value.trim();
100
101
  }
@@ -243,7 +243,11 @@ class WidgetService {
243
243
  WHERE id = ? AND user_id = ?`
244
244
  ).get(widgetId, userId);
245
245
  if (!row) return null;
246
- return this._serializeWidget(row, this._loadLatestSnapshotMap([widgetId]).get(widgetId) || null);
246
+ return this._serializeWidget(
247
+ row,
248
+ this._loadLatestSnapshotMap([widgetId]).get(widgetId) || null,
249
+ this._loadWidgetTasksMap([widgetId], userId).get(widgetId) || []
250
+ );
247
251
  }
248
252
 
249
253
  listWidgets(userId, { agentId = null } = {}) {
@@ -262,7 +266,8 @@ class WidgetService {
262
266
  ORDER BY updated_at DESC, created_at DESC`
263
267
  ).all(userId);
264
268
  const snapshotMap = this._loadLatestSnapshotMap(rows.map((row) => row.id));
265
- return rows.map((row) => this._serializeWidget(row, snapshotMap.get(row.id) || null));
269
+ const tasksMap = this._loadWidgetTasksMap(rows.map((row) => row.id), userId);
270
+ return rows.map((row) => this._serializeWidget(row, snapshotMap.get(row.id) || null, tasksMap.get(row.id) || []));
266
271
  }
267
272
 
268
273
  listLatestSnapshots(userId, { agentId = null } = {}) {
@@ -343,7 +348,7 @@ class WidgetService {
343
348
  throw new Error('Widget not found.');
344
349
  }
345
350
 
346
- const current = this._serializeWidget(existingRow, null);
351
+ const current = this._serializeWidget(existingRow, null, []);
347
352
  const normalized = normalizeWidgetInput({
348
353
  name: input.name ?? current.name,
349
354
  template: input.template ?? current.template,
@@ -581,7 +586,46 @@ class WidgetService {
581
586
  return map;
582
587
  }
583
588
 
584
- _serializeWidget(row, latestSnapshot) {
589
+ _loadWidgetTasksMap(widgetIds, userId) {
590
+ const ids = Array.from(new Set(widgetIds.filter(Boolean)));
591
+ const map = new Map();
592
+ if (!ids.length) return map;
593
+
594
+ const placeholders = ids.map(() => '?').join(', ');
595
+ const params = [userId, ...ids];
596
+
597
+ // We filter tasks where task_type is NOT 'widget_refresh'
598
+ // and where the task_config contains the widgetId.
599
+ const rows = db.prepare(
600
+ `SELECT id, name, trigger_type, enabled, task_config
601
+ FROM scheduled_tasks
602
+ WHERE user_id = ?
603
+ AND task_type != 'widget_refresh'
604
+ AND json_extract(task_config, '$.widgetId') IN (${placeholders})
605
+ ORDER BY created_at ASC`
606
+ ).all(...params);
607
+
608
+ for (const row of rows) {
609
+ const config = parseJsonObject(row.task_config, {});
610
+ const widgetId = config.widgetId;
611
+ if (!widgetId) continue;
612
+
613
+ const task = {
614
+ id: row.id,
615
+ name: row.name,
616
+ triggerType: row.trigger_type,
617
+ enabled: row.enabled !== 0 && row.enabled !== false,
618
+ };
619
+
620
+ if (!map.has(widgetId)) {
621
+ map.set(widgetId, []);
622
+ }
623
+ map.get(widgetId).push(task);
624
+ }
625
+ return map;
626
+ }
627
+
628
+ _serializeWidget(row, latestSnapshot, tasks = []) {
585
629
  const definition = parseJsonObject(row.definition_json, {});
586
630
  return {
587
631
  id: row.id,
@@ -600,6 +644,7 @@ class WidgetService {
600
644
  updatedAt: row.updated_at || null,
601
645
  nextRefresh: row.refresh_cron ? findNextRun(row.refresh_cron)?.toISOString() || null : null,
602
646
  latestSnapshot,
647
+ tasks,
603
648
  };
604
649
  }
605
650
  }
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+ const { getSessionSecret } = require('../services/account/session_secret');
5
+
6
+ const LOCAL_SECRET_PREFIX = 'enc:local:v1:';
7
+
8
+ function getLocalSecretMaterial() {
9
+ const configured = String(process.env.NEOAGENT_DATA_ENCRYPTION_KEY || '').trim();
10
+ if (configured) return configured;
11
+ return getSessionSecret();
12
+ }
13
+
14
+ function deriveKey() {
15
+ return crypto.createHash('sha256').update(getLocalSecretMaterial()).digest();
16
+ }
17
+
18
+ function isLocalEncryptedValue(value) {
19
+ return String(value || '').startsWith(LOCAL_SECRET_PREFIX);
20
+ }
21
+
22
+ function encryptLocalValue(value) {
23
+ const text = String(value || '');
24
+ if (!text) return '';
25
+ if (isLocalEncryptedValue(text)) return text;
26
+
27
+ const iv = crypto.randomBytes(12);
28
+ const cipher = crypto.createCipheriv('aes-256-gcm', deriveKey(), iv);
29
+ const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
30
+ const tag = cipher.getAuthTag();
31
+ return `${LOCAL_SECRET_PREFIX}${Buffer.concat([iv, tag, encrypted]).toString('base64')}`;
32
+ }
33
+
34
+ function decryptLocalValue(value) {
35
+ const text = String(value || '');
36
+ if (!text) return '';
37
+ if (!isLocalEncryptedValue(text)) return text;
38
+
39
+ try {
40
+ const payload = Buffer.from(text.slice(LOCAL_SECRET_PREFIX.length), 'base64');
41
+ const iv = payload.subarray(0, 12);
42
+ const tag = payload.subarray(12, 28);
43
+ const ciphertext = payload.subarray(28);
44
+ const decipher = crypto.createDecipheriv('aes-256-gcm', deriveKey(), iv);
45
+ decipher.setAuthTag(tag);
46
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8');
47
+ } catch {
48
+ return '';
49
+ }
50
+ }
51
+
52
+ module.exports = {
53
+ decryptLocalValue,
54
+ encryptLocalValue,
55
+ isLocalEncryptedValue,
56
+ };
@@ -1,14 +1,36 @@
1
1
  'use strict';
2
2
 
3
+ const SENSITIVE_KEY_RE = /(?:^|_|-)(?:token|secret|password|api[_-]?key|authorization|cookie|session(?:id)?|sid)(?:$|_|-)/i;
4
+
5
+ function redactSecrets(input) {
6
+ let text = String(input || '');
7
+ if (!text) return text;
8
+
9
+ text = text
10
+ .replace(/\b(Bearer\s+)[A-Za-z0-9._~+\/-]+=*/gi, '$1[redacted]')
11
+ .replace(/([?&](?:token|access_token|refresh_token|api[_-]?key|secret|password|authorization|cookie|session|sid)=)([^&#\s]+)/gi, '$1[redacted]')
12
+ .replace(/\b(token|access_token|refresh_token|authorization|api[_-]?key|secret|password|cookie|session(?:id)?|sid)\b\s*[:=]\s*("[^"]*"|'[^']*'|[^,\s;]+)/gi, '$1=[redacted]');
13
+
14
+ return text;
15
+ }
16
+
17
+ function sanitizeUrl(value) {
18
+ return redactSecrets(String(value || ''));
19
+ }
20
+
21
+ function isSensitiveKey(key) {
22
+ return SENSITIVE_KEY_RE.test(String(key || ''));
23
+ }
24
+
3
25
  function serializeValue(value, seen = new WeakSet()) {
4
26
  if (value instanceof Error) {
5
27
  return JSON.stringify({
6
28
  name: value.name,
7
- message: value.message,
8
- stack: value.stack,
29
+ message: redactSecrets(value.message),
30
+ stack: redactSecrets(value.stack),
9
31
  code: value.code,
10
32
  cause: value.cause instanceof Error
11
- ? { name: value.cause.name, message: value.cause.message, stack: value.cause.stack }
33
+ ? { name: value.cause.name, message: redactSecrets(value.cause.message), stack: redactSecrets(value.cause.stack) }
12
34
  : value.cause
13
35
  });
14
36
  }
@@ -22,7 +44,7 @@ function serializeValue(value, seen = new WeakSet()) {
22
44
  }
23
45
 
24
46
  if (!value || typeof value !== 'object') {
25
- return String(value);
47
+ return redactSecrets(String(value));
26
48
  }
27
49
 
28
50
  if (seen.has(value)) {
@@ -33,21 +55,27 @@ function serializeValue(value, seen = new WeakSet()) {
33
55
 
34
56
  try {
35
57
  return JSON.stringify(value, (key, nestedValue) => {
58
+ if (isSensitiveKey(key)) {
59
+ return '[redacted]';
60
+ }
36
61
  if (nestedValue instanceof Error) {
37
62
  return {
38
63
  name: nestedValue.name,
39
- message: nestedValue.message,
40
- stack: nestedValue.stack,
64
+ message: redactSecrets(nestedValue.message),
65
+ stack: redactSecrets(nestedValue.stack),
41
66
  code: nestedValue.code
42
67
  };
43
68
  }
44
69
  if (typeof nestedValue === 'bigint') {
45
70
  return nestedValue.toString();
46
71
  }
72
+ if (typeof nestedValue === 'string') {
73
+ return redactSecrets(nestedValue);
74
+ }
47
75
  return nestedValue;
48
76
  });
49
77
  } catch (err) {
50
- return `[Unserializable object: ${err.message}]`;
78
+ return `[Unserializable object: ${redactSecrets(err.message)}]`;
51
79
  } finally {
52
80
  seen.delete(value);
53
81
  }
@@ -58,14 +86,14 @@ function formatLogArgs(args) {
58
86
  }
59
87
 
60
88
  function logRequestSummary(level, req, message, extra = null) {
61
- const prefix = `[HTTP] ${req.method} ${req.originalUrl || req.url}`;
89
+ const prefix = `[HTTP] ${req.method} ${sanitizeUrl(req.originalUrl || req.url)}`;
62
90
  const summary = {
63
91
  ip: req.ip,
64
92
  userId: req.session?.userId || null,
65
93
  userAgent: req.get?.('user-agent') || null,
66
94
  ...extra
67
95
  };
68
- console[level](`${prefix} ${message}`, summary);
96
+ console[level](redactSecrets(`${prefix} ${message}`), summary);
69
97
  }
70
98
 
71
99
  /**