neoagent 2.3.1-beta.11 → 2.3.1-beta.111
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/.env.example +53 -5
- package/README.md +16 -7
- package/com.neoagent.plist +1 -1
- package/docs/automation.md +82 -41
- package/docs/capabilities.md +85 -89
- package/docs/configuration.md +87 -94
- package/docs/getting-started.md +46 -27
- package/docs/hardware.md +1 -1
- package/docs/index.md +24 -30
- package/docs/integrations.md +46 -64
- package/docs/migration.md +41 -146
- package/docs/operations.md +23 -20
- package/docs/skills.md +20 -20
- package/docs/why-neoagent.md +15 -13
- package/extensions/chrome-browser/background.mjs +10 -2
- package/flutter_app/.metadata +42 -0
- package/flutter_app/DESIGN_SYSTEM.md +166 -0
- package/flutter_app/README.md +21 -0
- package/flutter_app/analysis_options.yaml +32 -0
- package/flutter_app/android/app/build.gradle.kts +111 -0
- package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
- package/flutter_app/android/app/src/main/AndroidManifest.xml +185 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +888 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/auto/NeoAgentCarAppService.kt +114 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthConnectGateway.kt +280 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncNotifications.kt +113 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncPayload.kt +57 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncScheduler.kt +78 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncWorker.kt +253 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/PermissionsRationaleActivity.kt +46 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +21 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +586 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +78 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +104 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/telecom/NeoAgentConnection.kt +118 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/telecom/NeoAgentConnectionService.kt +86 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiHomeWidgetProvider.kt +457 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiWidgetStore.kt +194 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/VoiceLaunchWidgetProvider.kt +67 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetConfigActivity.kt +228 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncScheduler.kt +72 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncWorker.kt +186 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetTaskRunWorker.kt +210 -0
- package/flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
- package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_bg.xml +11 -0
- package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_task_bg.xml +8 -0
- package/flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
- package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget.xml +138 -0
- package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget_task_row.xml +52 -0
- package/flutter_app/android/app/src/main/res/layout/neoagent_voice_widget.xml +49 -0
- package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/values/arrays.xml +8 -0
- package/flutter_app/android/app/src/main/res/values/strings.xml +12 -0
- package/flutter_app/android/app/src/main/res/values/styles.xml +18 -0
- package/flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
- package/flutter_app/android/app/src/main/res/xml/automotive_app_desc.xml +4 -0
- package/flutter_app/android/app/src/main/res/xml/file_paths.xml +6 -0
- package/flutter_app/android/app/src/main/res/xml/neoagent_ai_widget_info.xml +12 -0
- package/flutter_app/android/app/src/main/res/xml/neoagent_voice_widget_info.xml +12 -0
- package/flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
- package/flutter_app/android/build.gradle.kts +24 -0
- package/flutter_app/android/ci-release.keystore +0 -0
- package/flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
- package/flutter_app/android/gradle.properties +3 -0
- package/flutter_app/android/key.properties +4 -0
- package/flutter_app/android/settings.gradle.kts +26 -0
- package/flutter_app/assets/branding/app_icon_1024.png +0 -0
- package/flutter_app/assets/branding/app_icon_128.png +0 -0
- package/flutter_app/assets/branding/app_icon_192.png +0 -0
- package/flutter_app/assets/branding/app_icon_256.png +0 -0
- package/flutter_app/assets/branding/app_icon_32.png +0 -0
- package/flutter_app/assets/branding/app_icon_512.png +0 -0
- package/flutter_app/assets/branding/app_icon_64.png +0 -0
- package/flutter_app/assets/branding/onboarding_intro.mp4 +0 -0
- package/flutter_app/assets/branding/tray_icon_template.png +0 -0
- package/flutter_app/lib/features/location/location_service.dart +117 -0
- package/flutter_app/lib/features/notifications/notification_interceptor.dart +73 -0
- package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +571 -0
- package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +275 -0
- package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +366 -0
- package/flutter_app/lib/features/onboarding/onboarding_shell.dart +57 -0
- package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +218 -0
- package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +46 -0
- package/flutter_app/lib/main.dart +107 -0
- package/flutter_app/lib/main_account_settings.dart +1340 -0
- package/flutter_app/lib/main_admin.dart +900 -0
- package/flutter_app/lib/main_app_shell.dart +2162 -0
- package/flutter_app/lib/main_chat.dart +3902 -0
- package/flutter_app/lib/main_controller.dart +7229 -0
- package/flutter_app/lib/main_devices.dart +1738 -0
- package/flutter_app/lib/main_integrations.dart +917 -0
- package/flutter_app/lib/main_launcher.dart +959 -0
- package/flutter_app/lib/main_launcher_entry.dart +5 -0
- package/flutter_app/lib/main_models.dart +3807 -0
- package/flutter_app/lib/main_navigation.dart +206 -0
- package/flutter_app/lib/main_operations.dart +5249 -0
- package/flutter_app/lib/main_recordings.dart +920 -0
- package/flutter_app/lib/main_runtime.dart +869 -0
- package/flutter_app/lib/main_settings.dart +2288 -0
- package/flutter_app/lib/main_shared.dart +3393 -0
- package/flutter_app/lib/main_spacing.dart +33 -0
- package/flutter_app/lib/main_theme.dart +276 -0
- package/flutter_app/lib/main_unified.dart +388 -0
- package/flutter_app/lib/main_voice_assistant.dart +957 -0
- package/flutter_app/lib/src/analytics_service.dart +294 -0
- package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
- package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
- package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
- package/flutter_app/lib/src/android_app_installer.dart +22 -0
- package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
- package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
- package/flutter_app/lib/src/android_auto_bridge.dart +59 -0
- package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
- package/flutter_app/lib/src/app_launch_bridge.dart +58 -0
- package/flutter_app/lib/src/app_release_updater.dart +511 -0
- package/flutter_app/lib/src/backend_client.dart +1870 -0
- package/flutter_app/lib/src/desktop_companion.dart +2 -0
- package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
- package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
- package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
- package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
- package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
- package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
- package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
- package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
- package/flutter_app/lib/src/health_bridge.dart +136 -0
- package/flutter_app/lib/src/live_voice_capture.dart +85 -0
- package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
- package/flutter_app/lib/src/network/app_http_client.dart +53 -0
- package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
- package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
- package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
- package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
- package/flutter_app/lib/src/oauth_launcher.dart +33 -0
- package/flutter_app/lib/src/oauth_launcher_io.dart +95 -0
- package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
- package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
- package/flutter_app/lib/src/recording_bridge.dart +232 -0
- package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
- package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
- package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
- package/flutter_app/lib/src/recording_payloads.dart +86 -0
- package/flutter_app/lib/src/theme/palette.dart +81 -0
- package/flutter_app/lib/src/web_app_update_monitor.dart +17 -0
- package/flutter_app/lib/src/web_app_update_monitor_stub.dart +24 -0
- package/flutter_app/lib/src/web_app_update_monitor_web.dart +123 -0
- package/flutter_app/lib/src/widget_bridge.dart +49 -0
- package/flutter_app/linux/CMakeLists.txt +128 -0
- package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
- package/flutter_app/linux/flutter/generated_plugin_registrant.cc +47 -0
- package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
- package/flutter_app/linux/flutter/generated_plugins.cmake +32 -0
- package/flutter_app/linux/runner/CMakeLists.txt +26 -0
- package/flutter_app/linux/runner/main.cc +6 -0
- package/flutter_app/linux/runner/my_application.cc +144 -0
- package/flutter_app/linux/runner/my_application.h +18 -0
- package/flutter_app/linux/runner/resources/app_icon.png +0 -0
- package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
- package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
- package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +48 -0
- package/flutter_app/macos/Podfile +42 -0
- package/flutter_app/macos/Podfile.lock +87 -0
- package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
- package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
- package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
- package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
- package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
- package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
- package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
- package/flutter_app/macos/Runner/Info.plist +36 -0
- package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
- package/flutter_app/macos/Runner/Release.entitlements +12 -0
- package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
- package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
- package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
- package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
- package/flutter_app/patch_strings.py +12 -0
- package/flutter_app/pubspec.lock +1256 -0
- package/flutter_app/pubspec.yaml +59 -0
- package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
- package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
- package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
- package/flutter_app/tool/generate_desktop_branding.py +219 -0
- package/flutter_app/web/favicon.png +0 -0
- package/flutter_app/web/favicon.svg +12 -0
- package/flutter_app/web/icons/Icon-192.png +0 -0
- package/flutter_app/web/icons/Icon-512.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
- package/flutter_app/web/index.html +39 -0
- package/flutter_app/web/manifest.json +35 -0
- package/flutter_app/windows/CMakeLists.txt +108 -0
- package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
- package/flutter_app/windows/flutter/generated_plugin_registrant.cc +50 -0
- package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
- package/flutter_app/windows/flutter/generated_plugins.cmake +36 -0
- package/flutter_app/windows/runner/CMakeLists.txt +41 -0
- package/flutter_app/windows/runner/Runner.rc +121 -0
- package/flutter_app/windows/runner/flutter_window.cpp +533 -0
- package/flutter_app/windows/runner/flutter_window.h +37 -0
- package/flutter_app/windows/runner/main.cpp +53 -0
- package/flutter_app/windows/runner/resource.h +16 -0
- package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
- package/flutter_app/windows/runner/runner.exe.manifest +14 -0
- package/flutter_app/windows/runner/utils.cpp +65 -0
- package/flutter_app/windows/runner/utils.h +19 -0
- package/flutter_app/windows/runner/win32_window.cpp +299 -0
- package/flutter_app/windows/runner/win32_window.h +102 -0
- package/lib/install_helpers.js +31 -0
- package/lib/manager.js +579 -122
- package/package.json +10 -3
- package/runtime/paths.js +137 -0
- package/server/config/analytics.js +30 -0
- package/server/db/database.js +296 -22
- package/server/guest-agent.android.package.json +13 -0
- package/server/guest-agent.browser.package.json +14 -0
- package/server/guest_agent.js +98 -52
- package/server/http/middleware.js +5 -2
- package/server/http/routes.js +3 -0
- package/server/http/static.js +41 -1
- package/server/index.js +15 -16
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/AssetManifest.bin +1 -1
- package/server/public/assets/AssetManifest.bin.json +1 -1
- package/server/public/assets/AssetManifest.json +1 -1
- package/server/public/assets/NOTICES +644 -155
- package/server/public/assets/assets/branding/onboarding_intro.mp4 +0 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/assets/packages/mixpanel_flutter/assets/mixpanel.js +3 -0
- package/server/public/canvaskit/wimp.wasm +0 -0
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/main.dart.js +96067 -88139
- package/server/routes/account.js +26 -1
- package/server/routes/agents.js +6 -2
- package/server/routes/android.js +22 -6
- package/server/routes/auth.js +25 -5
- package/server/routes/browser.js +47 -13
- package/server/routes/integrations.js +22 -1
- package/server/routes/memory.js +168 -6
- package/server/routes/runtime.js +14 -0
- package/server/routes/settings.js +102 -24
- package/server/routes/skills.js +12 -0
- package/server/routes/social_video.js +66 -0
- package/server/routes/tasks.js +22 -2
- package/server/routes/triggers.js +44 -35
- package/server/routes/wearable.js +67 -0
- package/server/routes/widgets.js +4 -4
- package/server/services/ai/capabilityHealth.js +23 -51
- package/server/services/ai/compaction.js +1 -1
- package/server/services/ai/deliverables/artifact_helpers.js +190 -0
- package/server/services/ai/deliverables/contracts.js +113 -0
- package/server/services/ai/deliverables/index.js +20 -0
- package/server/services/ai/deliverables/selector.js +94 -0
- package/server/services/ai/deliverables/validator.js +63 -0
- package/server/services/ai/deliverables/workflows.js +207 -0
- package/server/services/ai/engine.js +363 -27
- package/server/services/ai/hooks.js +127 -0
- package/server/services/ai/integrated_tools/index.js +98 -0
- package/server/services/ai/integrated_tools/remotion.js +373 -0
- package/server/services/ai/integrated_tools/shared.js +204 -0
- package/server/services/ai/integrated_tools/slidev.js +207 -0
- package/server/services/ai/loopPolicy.js +146 -0
- package/server/services/ai/models.js +31 -4
- package/server/services/ai/providers/claudeCode.js +49 -0
- package/server/services/ai/providers/openai.js +2 -1
- package/server/services/ai/providers/openaiCodex.js +422 -15
- package/server/services/ai/recordingInsights.js +11 -13
- package/server/services/ai/runEvents.js +100 -0
- package/server/services/ai/settings.js +12 -2
- package/server/services/ai/systemPrompt.js +15 -1
- package/server/services/ai/taskAnalysis.js +2 -0
- package/server/services/ai/toolResult.js +20 -0
- package/server/services/ai/toolRunner.js +46 -6
- package/server/services/ai/toolSelector.js +54 -1
- package/server/services/ai/tools.js +210 -116
- package/server/services/android/android_bootstrap_worker.js +63 -0
- package/server/services/android/controller.js +442 -1760
- package/server/services/bootstrap_helpers.js +0 -34
- package/server/services/browser/controller.js +279 -49
- package/server/services/cli/executor.js +40 -1
- package/server/services/desktop/screenRecorder.js +65 -9
- package/server/services/integrations/figma/provider.js +1 -0
- package/server/services/integrations/github/provider.js +3 -3
- package/server/services/integrations/google/provider.js +1 -0
- package/server/services/integrations/manager.js +25 -5
- package/server/services/integrations/microsoft/provider.js +1 -0
- package/server/services/integrations/oauth_provider.js +15 -5
- package/server/services/integrations/provider_config_store.js +22 -13
- package/server/services/integrations/registry.js +2 -4
- package/server/services/integrations/spotify/provider.js +1 -0
- package/server/services/integrations/trello/provider.js +842 -0
- package/server/services/manager.js +88 -155
- package/server/services/mcp/client.js +120 -23
- package/server/services/memory/llm_transfer.js +218 -0
- package/server/services/memory/manager.js +260 -33
- package/server/services/messaging/access_policy.js +10 -0
- package/server/services/messaging/automation.js +1 -1
- package/server/services/messaging/manager.js +64 -1
- package/server/services/messaging/meshtastic.js +277 -0
- package/server/services/messaging/meshtastic_env.js +40 -0
- package/server/services/messaging/meshtastic_protocol.js +530 -0
- package/server/services/messaging/meshtastic_tcp_transport.js +25 -0
- package/server/services/runtime/backends/local-vm.js +221 -158
- package/server/services/runtime/docker-vm-manager.js +392 -0
- package/server/services/runtime/guest_bootstrap.js +654 -0
- package/server/services/runtime/manager.js +85 -29
- package/server/services/runtime/settings.js +22 -31
- package/server/services/runtime/validation.js +16 -31
- package/server/services/social_video/adapters/base.js +26 -0
- package/server/services/social_video/adapters/index.js +27 -0
- package/server/services/social_video/adapters/instagram.js +17 -0
- package/server/services/social_video/adapters/tiktok.js +17 -0
- package/server/services/social_video/adapters/x.js +17 -0
- package/server/services/social_video/adapters/youtube.js +17 -0
- package/server/services/social_video/captions.js +187 -0
- package/server/services/social_video/frame.js +42 -0
- package/server/services/social_video/index.js +7 -0
- package/server/services/social_video/metadata.js +63 -0
- package/server/services/social_video/result.js +63 -0
- package/server/services/social_video/service.js +720 -0
- package/server/services/social_video/url.js +83 -0
- package/server/services/tasks/integration_runtime.js +1 -1
- package/server/services/tasks/runtime.js +1 -1
- package/server/services/tasks/task_repository.js +3 -6
- package/server/services/voice/openaiSpeech.js +6 -1
- package/server/services/voice/providers.js +142 -14
- package/server/services/voice/runtime.js +8 -8
- package/server/services/voice/runtimeManager.js +156 -19
- package/server/services/voice/turnRunner.js +29 -9
- package/server/services/wearable/firmware_manifest.js +353 -0
- package/server/services/wearable/gateway.js +350 -0
- package/server/services/wearable/protocol.js +45 -0
- package/server/services/wearable/service.js +240 -0
- package/server/services/websocket.js +3 -1
- package/server/services/widgets/focus_widget.js +137 -0
- package/server/services/widgets/service.js +133 -2
- package/server/services/workspace/manager.js +301 -0
- package/server/utils/deployment.js +16 -9
- package/server/services/integrations/home_assistant/provider.js +0 -630
- package/server/services/messaging/access_policy.test.js +0 -228
- package/server/services/runtime/backends/host.js +0 -37
- package/server/services/runtime/qemu.js +0 -391
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const { APP_DIR } = require('../../../../runtime/paths');
|
|
7
|
+
|
|
8
|
+
function ensureDir(dirPath) {
|
|
9
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
10
|
+
return dirPath;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function ensureRepoNodeModulesLink(targetDir) {
|
|
14
|
+
ensureDir(targetDir);
|
|
15
|
+
const sourceNodeModules = path.join(APP_DIR, 'node_modules');
|
|
16
|
+
const targetNodeModules = path.join(targetDir, 'node_modules');
|
|
17
|
+
if (fs.existsSync(targetNodeModules)) {
|
|
18
|
+
return targetNodeModules;
|
|
19
|
+
}
|
|
20
|
+
fs.symlinkSync(sourceNodeModules, targetNodeModules, 'junction');
|
|
21
|
+
return targetNodeModules;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function shellEscape(value) {
|
|
25
|
+
const text = String(value ?? '');
|
|
26
|
+
if (!text.length) return "''";
|
|
27
|
+
return `'${text.replace(/'/g, `'\\''`)}'`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normalizeFilenameBase(value, fallback = 'artifact') {
|
|
31
|
+
const normalized = String(value || '')
|
|
32
|
+
.trim()
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.replace(/[^a-z0-9_-]+/g, '-')
|
|
35
|
+
.replace(/^-+|-+$/g, '');
|
|
36
|
+
return normalized || fallback;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function resolveRepoBinary(...segments) {
|
|
40
|
+
const parts = [...segments];
|
|
41
|
+
if (process.platform === 'win32' && parts.length > 0 && !parts[parts.length - 1].endsWith('.cmd')) {
|
|
42
|
+
parts[parts.length - 1] = `${parts[parts.length - 1]}.cmd`;
|
|
43
|
+
}
|
|
44
|
+
return path.join(APP_DIR, 'node_modules', '.bin', ...parts);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function assertExistingFile(filePath, label = 'file') {
|
|
48
|
+
const absolutePath = path.resolve(String(filePath || ''));
|
|
49
|
+
if (!fs.existsSync(absolutePath)) {
|
|
50
|
+
throw new Error(`${label} does not exist: ${absolutePath}`);
|
|
51
|
+
}
|
|
52
|
+
const stat = fs.statSync(absolutePath);
|
|
53
|
+
if (!stat.isFile()) {
|
|
54
|
+
throw new Error(`${label} is not a file: ${absolutePath}`);
|
|
55
|
+
}
|
|
56
|
+
return absolutePath;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function writeTextFile(filePath, content) {
|
|
60
|
+
ensureDir(path.dirname(filePath));
|
|
61
|
+
fs.writeFileSync(filePath, String(content || ''), 'utf8');
|
|
62
|
+
return filePath;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function writeJsonFile(filePath, payload) {
|
|
66
|
+
writeTextFile(filePath, `${JSON.stringify(payload, null, 2)}\n`);
|
|
67
|
+
return filePath;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function copyAssetIntoJob(sourcePath, targetDir, preferredName = 'asset', workspaceManager = null, userId = null) {
|
|
71
|
+
if (!workspaceManager || userId == null) {
|
|
72
|
+
throw new Error('Workspace manager is required for integrated tool assets.');
|
|
73
|
+
}
|
|
74
|
+
const absolutePath = workspaceManager.resolvePath(userId, sourcePath, 'asset');
|
|
75
|
+
ensureDir(targetDir);
|
|
76
|
+
const extension = path.extname(absolutePath).toLowerCase();
|
|
77
|
+
const basename = normalizeFilenameBase(path.basename(absolutePath, extension), preferredName);
|
|
78
|
+
const filename = `${basename}-${crypto.randomBytes(3).toString('hex')}${extension}`;
|
|
79
|
+
const targetPath = path.join(targetDir, filename);
|
|
80
|
+
fs.copyFileSync(absolutePath, targetPath);
|
|
81
|
+
return {
|
|
82
|
+
sourcePath: absolutePath,
|
|
83
|
+
targetPath,
|
|
84
|
+
relativePath: filename,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function runCheckedCommand(executor, command, options = {}) {
|
|
89
|
+
if (!executor || typeof executor.execute !== 'function') {
|
|
90
|
+
throw new Error('CLI executor is unavailable for integrated tools.');
|
|
91
|
+
}
|
|
92
|
+
const result = await executor.execute(command, {
|
|
93
|
+
cwd: options.cwd,
|
|
94
|
+
timeout: options.timeout,
|
|
95
|
+
env: options.env,
|
|
96
|
+
});
|
|
97
|
+
if (result.exitCode !== 0) {
|
|
98
|
+
const stderr = String(result.stderr || '').trim();
|
|
99
|
+
const stdout = String(result.stdout || '').trim();
|
|
100
|
+
const details = stderr || stdout || `Command failed: ${command}`;
|
|
101
|
+
throw new Error(
|
|
102
|
+
options.errorPrefix
|
|
103
|
+
? `${options.errorPrefix} ${details}`.trim()
|
|
104
|
+
: details
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function createJobDir(toolName, filenameBase, workspaceManager = null, userId = null) {
|
|
111
|
+
const prefix = normalizeFilenameBase(toolName, 'tool');
|
|
112
|
+
const suffix = normalizeFilenameBase(filenameBase, 'output');
|
|
113
|
+
const jobId = `${Date.now()}-${crypto.randomBytes(4).toString('hex')}-${suffix}`;
|
|
114
|
+
if (workspaceManager && userId != null) {
|
|
115
|
+
const toolingRoot = await workspaceManager.getToolingRoot(userId, prefix);
|
|
116
|
+
return ensureDir(path.join(toolingRoot, jobId));
|
|
117
|
+
}
|
|
118
|
+
throw new Error('Workspace manager is required for integrated tool jobs.');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function inferArtifactExtension(filePath) {
|
|
122
|
+
return path.extname(String(filePath || '')).toLowerCase().replace(/^\./, '') || 'bin';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function inferContentType(filePath) {
|
|
126
|
+
const extension = path.extname(String(filePath || '')).toLowerCase();
|
|
127
|
+
switch (extension) {
|
|
128
|
+
case '.pdf': return 'application/pdf';
|
|
129
|
+
case '.ppt': return 'application/vnd.ms-powerpoint';
|
|
130
|
+
case '.pptx': return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
|
|
131
|
+
case '.md': return 'text/markdown';
|
|
132
|
+
case '.png': return 'image/png';
|
|
133
|
+
case '.jpg':
|
|
134
|
+
case '.jpeg': return 'image/jpeg';
|
|
135
|
+
case '.svg': return 'image/svg+xml';
|
|
136
|
+
case '.mp4': return 'video/mp4';
|
|
137
|
+
case '.mov': return 'video/quicktime';
|
|
138
|
+
case '.webm': return 'video/webm';
|
|
139
|
+
default: return 'application/octet-stream';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function inferArtifactKind(filePath) {
|
|
144
|
+
const extension = path.extname(String(filePath || '')).toLowerCase();
|
|
145
|
+
if (['.ppt', '.pptx'].includes(extension)) return 'slides';
|
|
146
|
+
if (['.pdf', '.md'].includes(extension)) return 'document';
|
|
147
|
+
if (['.png', '.jpg', '.jpeg', '.svg'].includes(extension)) return 'image';
|
|
148
|
+
if (['.mp4', '.mov', '.webm'].includes(extension)) return 'video';
|
|
149
|
+
return 'artifact';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function createArtifactDescriptor(filePath, options = {}) {
|
|
153
|
+
const absolutePath = assertExistingFile(filePath, 'output');
|
|
154
|
+
const stat = fs.statSync(absolutePath);
|
|
155
|
+
return {
|
|
156
|
+
kind: options.kind || inferArtifactKind(absolutePath),
|
|
157
|
+
label: options.label || path.basename(absolutePath),
|
|
158
|
+
path: absolutePath,
|
|
159
|
+
mimeType: options.mimeType || inferContentType(absolutePath),
|
|
160
|
+
size: stat.size,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function promoteArtifactDescriptor(descriptor, artifactStore, userId) {
|
|
165
|
+
if (!artifactStore || userId == null || !descriptor?.path) {
|
|
166
|
+
return descriptor;
|
|
167
|
+
}
|
|
168
|
+
const extension = inferArtifactExtension(descriptor.path);
|
|
169
|
+
const allocation = artifactStore.allocateFile(userId, {
|
|
170
|
+
kind: descriptor.kind || inferArtifactKind(descriptor.path),
|
|
171
|
+
extension,
|
|
172
|
+
contentType: descriptor.mimeType || inferContentType(descriptor.path),
|
|
173
|
+
filenameBase: normalizeFilenameBase(descriptor.label || descriptor.kind || 'artifact', 'artifact'),
|
|
174
|
+
originalFilename: path.basename(descriptor.path),
|
|
175
|
+
metadata: {
|
|
176
|
+
sourceTool: 'integrated-media',
|
|
177
|
+
originalPath: descriptor.path,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
fs.copyFileSync(descriptor.path, allocation.storagePath);
|
|
181
|
+
const finalized = artifactStore.finalizeFile(allocation.artifactId, allocation.storagePath);
|
|
182
|
+
return {
|
|
183
|
+
kind: descriptor.kind || inferArtifactKind(descriptor.path),
|
|
184
|
+
label: descriptor.label || path.basename(descriptor.path),
|
|
185
|
+
url: finalized.url,
|
|
186
|
+
mimeType: descriptor.mimeType || inferContentType(descriptor.path),
|
|
187
|
+
size: finalized.byteSize,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = {
|
|
192
|
+
copyAssetIntoJob,
|
|
193
|
+
createArtifactDescriptor,
|
|
194
|
+
createJobDir,
|
|
195
|
+
ensureDir,
|
|
196
|
+
ensureRepoNodeModulesLink,
|
|
197
|
+
normalizeFilenameBase,
|
|
198
|
+
promoteArtifactDescriptor,
|
|
199
|
+
resolveRepoBinary,
|
|
200
|
+
runCheckedCommand,
|
|
201
|
+
shellEscape,
|
|
202
|
+
writeJsonFile,
|
|
203
|
+
writeTextFile,
|
|
204
|
+
};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const {
|
|
6
|
+
copyAssetIntoJob,
|
|
7
|
+
createArtifactDescriptor,
|
|
8
|
+
createJobDir,
|
|
9
|
+
ensureDir,
|
|
10
|
+
ensureRepoNodeModulesLink,
|
|
11
|
+
normalizeFilenameBase,
|
|
12
|
+
promoteArtifactDescriptor,
|
|
13
|
+
resolveRepoBinary,
|
|
14
|
+
runCheckedCommand,
|
|
15
|
+
shellEscape,
|
|
16
|
+
writeTextFile,
|
|
17
|
+
} = require('./shared');
|
|
18
|
+
|
|
19
|
+
const SLIDEV_BIN = resolveRepoBinary('slidev');
|
|
20
|
+
const DEFAULT_EXPORT_FORMATS = ['pdf'];
|
|
21
|
+
const ALLOWED_EXPORT_FORMATS = new Set(['pdf', 'pptx', 'png']);
|
|
22
|
+
const ALLOWED_LAYOUTS = new Set(['cover', 'intro', 'statement', 'quote', 'section', 'two-cols', 'default']);
|
|
23
|
+
|
|
24
|
+
function normalizeArray(value) {
|
|
25
|
+
return Array.isArray(value) ? value : [];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function normalizeExportFormats(value) {
|
|
29
|
+
const formats = normalizeArray(value)
|
|
30
|
+
.map((item) => String(item || '').trim().toLowerCase())
|
|
31
|
+
.filter((item) => ALLOWED_EXPORT_FORMATS.has(item));
|
|
32
|
+
return formats.length > 0 ? [...new Set(formats)] : [...DEFAULT_EXPORT_FORMATS];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildFrontmatter(args = {}) {
|
|
36
|
+
const lines = [
|
|
37
|
+
'---',
|
|
38
|
+
`theme: ${String(args.theme || 'default').trim() || 'default'}`,
|
|
39
|
+
];
|
|
40
|
+
if (args.title) lines.push(`title: ${JSON.stringify(String(args.title).trim())}`);
|
|
41
|
+
if (args.subtitle) lines.push(`info: ${JSON.stringify(String(args.subtitle).trim())}`);
|
|
42
|
+
lines.push('layout: cover');
|
|
43
|
+
lines.push('transition: fade-out');
|
|
44
|
+
lines.push('mdc: true');
|
|
45
|
+
lines.push('---');
|
|
46
|
+
return lines.join('\n');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function materializeSlideAsset(value, assetsDir, workspaceManager, userId) {
|
|
50
|
+
if (!value) return '';
|
|
51
|
+
const text = String(value).trim();
|
|
52
|
+
if (!text) return '';
|
|
53
|
+
if (/^https?:\/\//i.test(text)) {
|
|
54
|
+
return text;
|
|
55
|
+
}
|
|
56
|
+
const asset = copyAssetIntoJob(text, assetsDir, 'slide-asset', workspaceManager, userId);
|
|
57
|
+
return `./assets/${asset.relativePath}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function buildStructuredDeckMarkdown(args, jobDir, workspaceManager, userId) {
|
|
61
|
+
const slides = normalizeArray(args.slides);
|
|
62
|
+
if (slides.length === 0) {
|
|
63
|
+
throw new Error('generate_slide_deck requires a non-empty slides array or deck_markdown.');
|
|
64
|
+
}
|
|
65
|
+
const assetsDir = ensureDir(path.join(jobDir, 'assets'));
|
|
66
|
+
const sections = [buildFrontmatter(args)];
|
|
67
|
+
const coverTitle = String(args.title || 'Presentation').trim();
|
|
68
|
+
const coverSubtitle = String(args.subtitle || '').trim();
|
|
69
|
+
sections.push(`# ${coverTitle}${coverSubtitle ? `\n\n${coverSubtitle}` : ''}`);
|
|
70
|
+
|
|
71
|
+
for (const slide of slides) {
|
|
72
|
+
const title = String(slide?.title || '').trim();
|
|
73
|
+
const layout = ALLOWED_LAYOUTS.has(String(slide?.layout || '').trim()) ? String(slide.layout).trim() : 'default';
|
|
74
|
+
const body = String(slide?.body || '').trim();
|
|
75
|
+
const notes = String(slide?.notes || '').trim();
|
|
76
|
+
const bullets = normalizeArray(slide?.bullets)
|
|
77
|
+
.map((item) => String(item || '').trim())
|
|
78
|
+
.filter(Boolean);
|
|
79
|
+
const imageRef = materializeSlideAsset(slide?.image_path || slide?.image_url, assetsDir, workspaceManager, userId);
|
|
80
|
+
const slideLines = ['---'];
|
|
81
|
+
if (layout !== 'default') slideLines.push(`layout: ${layout}`);
|
|
82
|
+
if (slide?.className) slideLines.push(`class: ${JSON.stringify(String(slide.className).trim())}`);
|
|
83
|
+
slideLines.push('---');
|
|
84
|
+
if (title) slideLines.push(`# ${title}`);
|
|
85
|
+
if (body) slideLines.push('', body);
|
|
86
|
+
if (bullets.length > 0) {
|
|
87
|
+
if (!body) slideLines.push('');
|
|
88
|
+
slideLines.push(...bullets.map((item) => `- ${item}`));
|
|
89
|
+
}
|
|
90
|
+
if (imageRef) {
|
|
91
|
+
slideLines.push('', ``);
|
|
92
|
+
}
|
|
93
|
+
if (notes) {
|
|
94
|
+
slideLines.push('', `<!--\n${notes}\n-->`);
|
|
95
|
+
}
|
|
96
|
+
sections.push(slideLines.join('\n'));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return `${sections.join('\n\n')}\n`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function buildDeckMarkdown(args, jobDir, workspaceManager, userId) {
|
|
103
|
+
const rawMarkdown = String(args.deck_markdown || '').trim();
|
|
104
|
+
if (rawMarkdown) {
|
|
105
|
+
if (rawMarkdown.startsWith('---')) {
|
|
106
|
+
return `${rawMarkdown}\n`;
|
|
107
|
+
}
|
|
108
|
+
return `${buildFrontmatter(args)}\n\n${rawMarkdown}\n`;
|
|
109
|
+
}
|
|
110
|
+
return buildStructuredDeckMarkdown(args, jobDir, workspaceManager, userId);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function resolveExportPath(jobDir, filenameBase, format) {
|
|
114
|
+
if (format === 'png') {
|
|
115
|
+
return path.join(jobDir, `${filenameBase}-png`);
|
|
116
|
+
}
|
|
117
|
+
return path.join(jobDir, `${filenameBase}.${format}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function collectPngArtifacts(outputDir) {
|
|
121
|
+
if (!fs.existsSync(outputDir)) return [];
|
|
122
|
+
return fs.readdirSync(outputDir)
|
|
123
|
+
.filter((entry) => entry.toLowerCase().endsWith('.png'))
|
|
124
|
+
.sort()
|
|
125
|
+
.map((entry) => createArtifactDescriptor(path.join(outputDir, entry), {
|
|
126
|
+
kind: 'image',
|
|
127
|
+
label: entry,
|
|
128
|
+
mimeType: 'image/png',
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function generateSlideDeck(args, context = {}) {
|
|
133
|
+
if (!fs.existsSync(SLIDEV_BIN)) {
|
|
134
|
+
throw new Error('Slidev CLI is not installed.');
|
|
135
|
+
}
|
|
136
|
+
const filenameBase = normalizeFilenameBase(args.filename_base || args.title || 'slide-deck', 'slide-deck');
|
|
137
|
+
const workspaceManager = context.workspaceManager;
|
|
138
|
+
const userId = context.userId;
|
|
139
|
+
const jobDir = await createJobDir('slidev', filenameBase, workspaceManager, userId);
|
|
140
|
+
ensureRepoNodeModulesLink(jobDir);
|
|
141
|
+
const markdownPath = path.join(jobDir, `${filenameBase}.md`);
|
|
142
|
+
const exportFormats = normalizeExportFormats(args.export_formats);
|
|
143
|
+
const deckMarkdown = buildDeckMarkdown(args, jobDir, workspaceManager, userId);
|
|
144
|
+
writeTextFile(markdownPath, deckMarkdown);
|
|
145
|
+
|
|
146
|
+
const executor = context.cliExecutor;
|
|
147
|
+
const commandOutputs = [];
|
|
148
|
+
for (const format of exportFormats) {
|
|
149
|
+
const outputPath = resolveExportPath(jobDir, filenameBase, format);
|
|
150
|
+
const command = [
|
|
151
|
+
shellEscape(SLIDEV_BIN),
|
|
152
|
+
'export',
|
|
153
|
+
shellEscape(markdownPath),
|
|
154
|
+
'--format',
|
|
155
|
+
shellEscape(format),
|
|
156
|
+
'--output',
|
|
157
|
+
shellEscape(outputPath),
|
|
158
|
+
].join(' ');
|
|
159
|
+
const result = await runCheckedCommand(executor, command, {
|
|
160
|
+
cwd: jobDir,
|
|
161
|
+
timeout: 15 * 60 * 1000,
|
|
162
|
+
errorPrefix: `Slidev export failed for ${format}.`,
|
|
163
|
+
});
|
|
164
|
+
commandOutputs.push({ format, result });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const descriptors = [
|
|
168
|
+
createArtifactDescriptor(markdownPath, {
|
|
169
|
+
kind: 'document',
|
|
170
|
+
label: path.basename(markdownPath),
|
|
171
|
+
mimeType: 'text/markdown',
|
|
172
|
+
}),
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
for (const format of exportFormats) {
|
|
176
|
+
const outputPath = resolveExportPath(jobDir, filenameBase, format);
|
|
177
|
+
if (format === 'png') {
|
|
178
|
+
descriptors.push(...collectPngArtifacts(outputPath));
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
descriptors.push(createArtifactDescriptor(outputPath, {
|
|
182
|
+
kind: format === 'pptx' ? 'slides' : 'document',
|
|
183
|
+
label: path.basename(outputPath),
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const promotedArtifacts = descriptors.map((descriptor) => (
|
|
188
|
+
promoteArtifactDescriptor(descriptor, context.artifactStore, context.userId)
|
|
189
|
+
));
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
success: true,
|
|
193
|
+
tool: 'generate_slide_deck',
|
|
194
|
+
title: String(args.title || '').trim() || null,
|
|
195
|
+
exportFormats,
|
|
196
|
+
artifacts: promotedArtifacts,
|
|
197
|
+
message: `Generated slide deck with ${exportFormats.join(', ')} output.`,
|
|
198
|
+
logs: commandOutputs.map((item) => ({
|
|
199
|
+
format: item.format,
|
|
200
|
+
durationMs: item.result.durationMs,
|
|
201
|
+
})),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module.exports = {
|
|
206
|
+
generateSlideDeck,
|
|
207
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loopPolicy.js
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for every tunable limit in the agent loop.
|
|
5
|
+
* No magic numbers live in engine.js — everything flows from here.
|
|
6
|
+
*
|
|
7
|
+
* Values resolve in priority order:
|
|
8
|
+
* 1. Per-run option override (options.*)
|
|
9
|
+
* 2. Agent AI settings (aiSettings.*)
|
|
10
|
+
* 3. Hardcoded sane default
|
|
11
|
+
*
|
|
12
|
+
* "Open but stable": limits exist as safety nets, not as the primary
|
|
13
|
+
* exit signal. The AI signals completion via task_complete; these
|
|
14
|
+
* numbers only fire when something goes wrong.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const DEFAULT_MAX_ITERATIONS = 20;
|
|
18
|
+
const DEFAULT_WIDGET_MAX_ITERATIONS = 30;
|
|
19
|
+
const DEFAULT_PLAN_EXECUTE_MAX_ITERATIONS = 40;
|
|
20
|
+
const DEFAULT_COMPACTION_THRESHOLD = 0.82;
|
|
21
|
+
const DEFAULT_MAX_CONSECUTIVE_TOOL_FAILURES = 5;
|
|
22
|
+
const DEFAULT_MAX_MODEL_FAILURE_RECOVERIES = 3;
|
|
23
|
+
|
|
24
|
+
// Hard ceilings — protect against runaway config values
|
|
25
|
+
const MAX_ALLOWED_ITERATIONS = 200;
|
|
26
|
+
const MAX_ALLOWED_TOOL_FAILURES = 50;
|
|
27
|
+
const MAX_ALLOWED_MODEL_RECOVERIES = 10;
|
|
28
|
+
const MAX_ALLOWED_BUDGET_CHARS = 500_000;
|
|
29
|
+
|
|
30
|
+
/** Return n if finite and positive, otherwise fallback. */
|
|
31
|
+
function finitePositive(n, fallback) {
|
|
32
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Clamp n to [lo, hi]; return fallback if not finite. */
|
|
36
|
+
function clampFinite(n, lo, hi, fallback) {
|
|
37
|
+
if (!Number.isFinite(n)) return fallback;
|
|
38
|
+
return Math.min(Math.max(n, lo), hi);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {object} aiSettings - from getAiSettings()
|
|
43
|
+
* @param {string} triggerType - 'chat' | 'schedule' | 'subagent' | etc.
|
|
44
|
+
* @param {string} analysisMode - 'direct_answer' | 'execute' | 'plan_execute'
|
|
45
|
+
* @param {object} options - per-run options (may override anything)
|
|
46
|
+
* @returns {LoopPolicy}
|
|
47
|
+
*/
|
|
48
|
+
function buildLoopPolicy(aiSettings = {}, triggerType = 'chat', analysisMode = 'execute', options = {}) {
|
|
49
|
+
// ── maxIterations ────────────────────────────────────────────────────────
|
|
50
|
+
// Resolve raw value from options → aiSettings → mode/context defaults,
|
|
51
|
+
// then clamp to [1, MAX_ALLOWED_ITERATIONS] and floor to integer.
|
|
52
|
+
let rawIterations;
|
|
53
|
+
if (options.maxIterations != null) {
|
|
54
|
+
rawIterations = Number(options.maxIterations);
|
|
55
|
+
} else if (aiSettings.max_iterations != null) {
|
|
56
|
+
rawIterations = Number(aiSettings.max_iterations);
|
|
57
|
+
} else if (options.widgetId) {
|
|
58
|
+
rawIterations = DEFAULT_WIDGET_MAX_ITERATIONS;
|
|
59
|
+
} else if (analysisMode === 'plan_execute') {
|
|
60
|
+
rawIterations = DEFAULT_PLAN_EXECUTE_MAX_ITERATIONS;
|
|
61
|
+
} else {
|
|
62
|
+
rawIterations = DEFAULT_MAX_ITERATIONS;
|
|
63
|
+
}
|
|
64
|
+
const maxIterations = clampFinite(
|
|
65
|
+
Math.floor(rawIterations),
|
|
66
|
+
1,
|
|
67
|
+
MAX_ALLOWED_ITERATIONS,
|
|
68
|
+
DEFAULT_MAX_ITERATIONS,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// ── Tool result size budget ───────────────────────────────────────────────
|
|
72
|
+
// Must be a finite positive integer; bad values fall back to 2400.
|
|
73
|
+
const defaultBudget = clampFinite(
|
|
74
|
+
Math.floor(Number(aiSettings.tool_replay_budget_chars) || 0),
|
|
75
|
+
500,
|
|
76
|
+
MAX_ALLOWED_BUDGET_CHARS,
|
|
77
|
+
2400,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// ── Scalar policy fields ─────────────────────────────────────────────────
|
|
81
|
+
const maxConsecutiveToolFailures = clampFinite(
|
|
82
|
+
Math.floor(Number(aiSettings.max_consecutive_tool_failures)),
|
|
83
|
+
1,
|
|
84
|
+
MAX_ALLOWED_TOOL_FAILURES,
|
|
85
|
+
DEFAULT_MAX_CONSECUTIVE_TOOL_FAILURES,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const maxModelFailureRecoveries = clampFinite(
|
|
89
|
+
Math.floor(Number(aiSettings.max_model_failure_recoveries)),
|
|
90
|
+
0,
|
|
91
|
+
MAX_ALLOWED_MODEL_RECOVERIES,
|
|
92
|
+
DEFAULT_MAX_MODEL_FAILURE_RECOVERIES,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// compactionThreshold must be in (0, 1]; clamp to [0.1, 1].
|
|
96
|
+
const compactionThreshold = clampFinite(
|
|
97
|
+
Number(aiSettings.compaction_threshold),
|
|
98
|
+
0.1,
|
|
99
|
+
1,
|
|
100
|
+
DEFAULT_COMPACTION_THRESHOLD,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
maxIterations,
|
|
105
|
+
maxConsecutiveToolFailures,
|
|
106
|
+
maxModelFailureRecoveries,
|
|
107
|
+
|
|
108
|
+
// Fill ratio at which context compaction triggers (0–1)
|
|
109
|
+
compactionThreshold,
|
|
110
|
+
|
|
111
|
+
// Per-category tool result size budgets (chars)
|
|
112
|
+
toolResultBudget: {
|
|
113
|
+
default: defaultBudget,
|
|
114
|
+
file: clampFinite(Math.floor(Number(aiSettings.tool_replay_budget_file_chars)), 500, MAX_ALLOWED_BUDGET_CHARS, Math.max(defaultBudget, 6000)),
|
|
115
|
+
browser: clampFinite(Math.floor(Number(aiSettings.tool_replay_budget_browser_chars)), 500, MAX_ALLOWED_BUDGET_CHARS, Math.max(defaultBudget, 4000)),
|
|
116
|
+
command: clampFinite(Math.floor(Number(aiSettings.tool_replay_budget_command_chars)), 500, MAX_ALLOWED_BUDGET_CHARS, Math.max(defaultBudget, 4000)),
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
// Hard ceiling is always 2× soft, capped at a reasonable absolute max
|
|
120
|
+
hardLimitMultiplier: 2,
|
|
121
|
+
absoluteHardLimit: 12000,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Map a tool name to its result-size category.
|
|
127
|
+
*/
|
|
128
|
+
function getToolCategory(toolName) {
|
|
129
|
+
if (!toolName) return 'default';
|
|
130
|
+
if (/^(read_file|write_file|search_files|list_directory|file_)/.test(toolName)) return 'file';
|
|
131
|
+
if (/^browser_/.test(toolName)) return 'browser';
|
|
132
|
+
if (/^(execute_command|android_shell|android_)/.test(toolName)) return 'command';
|
|
133
|
+
return 'default';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Resolve soft + hard limits for a specific tool from the policy.
|
|
138
|
+
*/
|
|
139
|
+
function resolveToolResultLimits(toolName, policy) {
|
|
140
|
+
const category = getToolCategory(toolName);
|
|
141
|
+
const soft = policy.toolResultBudget[category] ?? policy.toolResultBudget.default;
|
|
142
|
+
const hard = Math.min(soft * policy.hardLimitMultiplier, policy.absoluteHardLimit);
|
|
143
|
+
return { softLimit: soft, hardLimit: hard };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
module.exports = { buildLoopPolicy, getToolCategory, resolveToolResultLimits };
|
|
@@ -5,6 +5,7 @@ const { OllamaProvider } = require('./providers/ollama');
|
|
|
5
5
|
const { OpenAIProvider } = require('./providers/openai');
|
|
6
6
|
const { GithubCopilotProvider } = require('./providers/githubCopilot');
|
|
7
7
|
const { OpenAICodexProvider } = require('./providers/openaiCodex');
|
|
8
|
+
const { ClaudeCodeProvider } = require('./providers/claudeCode');
|
|
8
9
|
const {
|
|
9
10
|
AI_PROVIDER_DEFINITIONS,
|
|
10
11
|
getProviderConfigs,
|
|
@@ -31,17 +32,23 @@ const STATIC_MODELS = [
|
|
|
31
32
|
purpose: 'coding'
|
|
32
33
|
},
|
|
33
34
|
{
|
|
34
|
-
id: 'gpt-5.
|
|
35
|
-
label: 'GPT-5.
|
|
35
|
+
id: 'gpt-5.5',
|
|
36
|
+
label: 'GPT-5.5 (Codex Default)',
|
|
36
37
|
provider: 'openai-codex',
|
|
37
38
|
purpose: 'general'
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
|
-
id: 'gpt-4
|
|
41
|
-
label: 'GPT-4
|
|
41
|
+
id: 'gpt-5.4-mini',
|
|
42
|
+
label: 'GPT-5.4 mini (Codex Fast)',
|
|
42
43
|
provider: 'openai-codex',
|
|
43
44
|
purpose: 'coding'
|
|
44
45
|
},
|
|
46
|
+
{
|
|
47
|
+
id: 'gpt-5.4',
|
|
48
|
+
label: 'GPT-5.4 (Codex Fallback)',
|
|
49
|
+
provider: 'openai-codex',
|
|
50
|
+
purpose: 'general'
|
|
51
|
+
},
|
|
45
52
|
{
|
|
46
53
|
id: 'gpt-5-nano',
|
|
47
54
|
label: 'GPT-5 Nano (Fast / Subagents)',
|
|
@@ -66,6 +73,24 @@ const STATIC_MODELS = [
|
|
|
66
73
|
provider: 'anthropic',
|
|
67
74
|
purpose: 'fast'
|
|
68
75
|
},
|
|
76
|
+
{
|
|
77
|
+
id: 'claude-opus-4-7',
|
|
78
|
+
label: 'Claude Opus 4.7 (Claude Code / Default)',
|
|
79
|
+
provider: 'claude-code',
|
|
80
|
+
purpose: 'general'
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'claude-sonnet-4-6',
|
|
84
|
+
label: 'Claude Sonnet 4.6 (Claude Code / Fast)',
|
|
85
|
+
provider: 'claude-code',
|
|
86
|
+
purpose: 'coding'
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'claude-haiku-4-5-20251001',
|
|
90
|
+
label: 'Claude Haiku 4.5 (Claude Code / Subagents)',
|
|
91
|
+
provider: 'claude-code',
|
|
92
|
+
purpose: 'fast'
|
|
93
|
+
},
|
|
69
94
|
{
|
|
70
95
|
id: 'gemini-3.1-flash-lite-preview',
|
|
71
96
|
label: 'Gemini 3.1 Flash Lite (Preview)',
|
|
@@ -332,6 +357,8 @@ function createProviderInstance(providerStr, userId = null, configOverrides = {}
|
|
|
332
357
|
return new GithubCopilotProvider({ apiKey: runtime.apiKey, ...providerOverrides });
|
|
333
358
|
} else if (providerStr === 'openai-codex') {
|
|
334
359
|
return new OpenAICodexProvider({ apiKey: runtime.apiKey, ...providerOverrides });
|
|
360
|
+
} else if (providerStr === 'claude-code') {
|
|
361
|
+
return new ClaudeCodeProvider({ apiKey: runtime.apiKey, ...providerOverrides });
|
|
335
362
|
}
|
|
336
363
|
throw new Error(`Unknown provider: ${providerStr}`);
|
|
337
364
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const Anthropic = require('@anthropic-ai/sdk');
|
|
5
|
+
const { AnthropicProvider } = require('./anthropic');
|
|
6
|
+
|
|
7
|
+
const CLAUDE_CLI_CREDS_PATH = path.join(os.homedir(), '.claude', '.credentials.json');
|
|
8
|
+
const CLAUDE_CODE_BASE_URL = 'https://api.claude.ai/api';
|
|
9
|
+
|
|
10
|
+
function readClaudeCliToken() {
|
|
11
|
+
try {
|
|
12
|
+
const raw = fs.readFileSync(CLAUDE_CLI_CREDS_PATH, 'utf8');
|
|
13
|
+
const data = JSON.parse(raw);
|
|
14
|
+
const token = data?.claudeAiOauthTokens?.accessToken;
|
|
15
|
+
return typeof token === 'string' && token ? token : null;
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class ClaudeCodeProvider extends AnthropicProvider {
|
|
22
|
+
constructor(config = {}) {
|
|
23
|
+
super(config);
|
|
24
|
+
this.name = 'claude-code';
|
|
25
|
+
this.models = [
|
|
26
|
+
'claude-opus-4-7',
|
|
27
|
+
'claude-sonnet-4-6',
|
|
28
|
+
'claude-haiku-4-5-20251001',
|
|
29
|
+
];
|
|
30
|
+
this.contextWindows = {
|
|
31
|
+
'claude-opus-4-7': 200000,
|
|
32
|
+
'claude-sonnet-4-6': 200000,
|
|
33
|
+
'claude-haiku-4-5-20251001': 200000,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const authToken = config.apiKey || process.env.CLAUDE_CODE_OAUTH_TOKEN || readClaudeCliToken();
|
|
37
|
+
if (!authToken) {
|
|
38
|
+
console.warn('[ClaudeCode] No access token. Run `neoagent login claude-code` to authenticate.');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// OAuth tokens use Authorization: Bearer — authToken option sends that header
|
|
42
|
+
this.client = new Anthropic({
|
|
43
|
+
authToken: authToken || undefined,
|
|
44
|
+
baseURL: config.baseUrl || CLAUDE_CODE_BASE_URL,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { ClaudeCodeProvider, readClaudeCliToken };
|
|
@@ -29,7 +29,8 @@ class OpenAIProvider extends BaseProvider {
|
|
|
29
29
|
};
|
|
30
30
|
this.client = new OpenAI({
|
|
31
31
|
apiKey: config.apiKey || process.env.OPENAI_API_KEY,
|
|
32
|
-
baseURL: config.baseUrl || process.env.OPENAI_BASE_URL || undefined
|
|
32
|
+
baseURL: config.baseUrl || process.env.OPENAI_BASE_URL || undefined,
|
|
33
|
+
defaultHeaders: config.defaultHeaders || undefined
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
|