neoagent 2.3.1-beta.62 → 2.3.1-beta.64
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 +9 -0
- package/docs/configuration.md +11 -0
- package/flutter_app/lib/main.dart +2 -0
- package/flutter_app/lib/main_account_settings.dart +50 -22
- package/flutter_app/lib/main_admin.dart +24 -10
- package/flutter_app/lib/main_app_shell.dart +10 -9
- package/flutter_app/lib/main_chat.dart +433 -309
- package/flutter_app/lib/main_controller.dart +164 -6
- package/flutter_app/lib/main_integrations.dart +15 -7
- package/flutter_app/lib/main_models.dart +116 -7
- package/flutter_app/lib/main_navigation.dart +27 -18
- package/flutter_app/lib/main_operations.dart +162 -91
- package/flutter_app/lib/main_runtime.dart +22 -0
- package/flutter_app/lib/main_settings.dart +287 -75
- package/flutter_app/lib/main_shared.dart +165 -6
- package/flutter_app/lib/main_unified.dart +388 -0
- package/flutter_app/lib/src/analytics_service.dart +294 -0
- package/flutter_app/lib/src/backend_client.dart +4 -0
- package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +2 -0
- package/flutter_app/pubspec.lock +8 -0
- package/flutter_app/pubspec.yaml +1 -0
- package/flutter_app/web/index.html +1 -0
- package/package.json +1 -1
- package/server/config/analytics.js +30 -0
- package/server/db/database.js +52 -0
- package/server/http/routes.js +1 -0
- 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/NOTICES +183 -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/flutter_bootstrap.js +1 -1
- package/server/public/index.html +1 -0
- package/server/public/main.dart.js +83685 -82046
- package/server/routes/agents.js +2 -1
- package/server/routes/memory.js +75 -3
- package/server/routes/runtime.js +14 -0
- package/server/routes/widgets.js +4 -4
- package/server/services/ai/engine.js +86 -0
- package/server/services/ai/runEvents.js +100 -0
- package/server/services/memory/manager.js +242 -26
- package/server/services/websocket.js +3 -1
- package/server/services/widgets/focus_widget.js +126 -0
- package/server/services/widgets/service.js +130 -2
|
@@ -37,6 +37,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
37
37
|
final OAuthLauncher _oauthLauncher;
|
|
38
38
|
final app_release_updater.AppReleaseUpdater _appReleaseUpdater =
|
|
39
39
|
app_release_updater.AppReleaseUpdater();
|
|
40
|
+
final AppAnalytics _analytics = AppAnalytics();
|
|
40
41
|
final LiveVoiceCapture _liveVoiceCapture = LiveVoiceCapture();
|
|
41
42
|
final DesktopScreenCapture _desktopScreenCapture =
|
|
42
43
|
createDesktopScreenCapture();
|
|
@@ -98,6 +99,10 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
98
99
|
bool networkStatusKnown = false;
|
|
99
100
|
bool _desktopFloatingToolbarPopupRequested = false;
|
|
100
101
|
bool _voiceAssistantIncludeScreenContext = false;
|
|
102
|
+
bool _didTrackAppOpen = false;
|
|
103
|
+
bool _analyticsConfigured = false;
|
|
104
|
+
bool _analyticsConsentResolved = false;
|
|
105
|
+
bool _analyticsConsentGranted = false;
|
|
101
106
|
|
|
102
107
|
bool hasUser = true;
|
|
103
108
|
bool registrationOpen = false;
|
|
@@ -319,6 +324,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
319
324
|
_diagnosticLogSubscription?.cancel();
|
|
320
325
|
_connectivitySubscription?.cancel();
|
|
321
326
|
_appReleaseUpdater.dispose();
|
|
327
|
+
unawaited(_analytics.dispose());
|
|
322
328
|
_desktopCompanion.removeListener(notifyListeners);
|
|
323
329
|
unawaited(_desktopCompanion.disconnect());
|
|
324
330
|
_recordingBridge.removeListener(_handleRecordingBridgeChanged);
|
|
@@ -349,6 +355,12 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
349
355
|
bool get canCaptureVoiceAssistantScreenContext =>
|
|
350
356
|
_desktopScreenCapture.isSupported;
|
|
351
357
|
|
|
358
|
+
bool get showAnalyticsConsentBanner =>
|
|
359
|
+
kIsWeb && _analyticsConfigured && !_analyticsConsentResolved;
|
|
360
|
+
|
|
361
|
+
bool get analyticsConsentGranted =>
|
|
362
|
+
_analyticsConsentResolved && _analyticsConsentGranted;
|
|
363
|
+
|
|
352
364
|
bool get isLiveVoiceCaptureEngaged =>
|
|
353
365
|
_isStartingLiveVoice || _liveVoiceCaptureActive;
|
|
354
366
|
|
|
@@ -701,6 +713,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
701
713
|
appUpdateAutoCheckEnabled =
|
|
702
714
|
_prefs?.getBool('app.update.autoCheckEnabled') ?? true;
|
|
703
715
|
installedAppVersion = await _safeLoadInstalledAppVersion();
|
|
716
|
+
await _initializeAnalytics();
|
|
704
717
|
await refreshConnectivityStatus();
|
|
705
718
|
if (_connectivityPluginAvailable && _connectivitySubscription == null) {
|
|
706
719
|
try {
|
|
@@ -862,6 +875,101 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
862
875
|
}
|
|
863
876
|
}
|
|
864
877
|
|
|
878
|
+
Future<void> _initializeAnalytics() async {
|
|
879
|
+
try {
|
|
880
|
+
final runtimeConfig = await _backendClient.fetchRuntimeConfig(backendUrl);
|
|
881
|
+
final analyticsConfig = runtimeConfig['analytics'];
|
|
882
|
+
final mixpanelConfig = analyticsConfig is Map
|
|
883
|
+
? analyticsConfig['mixpanel']
|
|
884
|
+
: null;
|
|
885
|
+
final token = mixpanelConfig is Map
|
|
886
|
+
? mixpanelConfig['token']?.toString()
|
|
887
|
+
: null;
|
|
888
|
+
_analyticsConfigured = token != null && token.trim().isNotEmpty;
|
|
889
|
+
final consentState = _prefs?.getBool('analytics.cookieConsent');
|
|
890
|
+
_analyticsConsentResolved = consentState != null;
|
|
891
|
+
_analyticsConsentGranted = consentState == true;
|
|
892
|
+
await _analytics.initialize(
|
|
893
|
+
token: token,
|
|
894
|
+
consentGranted: _analyticsConsentGranted,
|
|
895
|
+
);
|
|
896
|
+
_trackAppOpenedIfNeeded();
|
|
897
|
+
} catch (_) {
|
|
898
|
+
_analyticsConfigured = false;
|
|
899
|
+
_analyticsConsentResolved = false;
|
|
900
|
+
_analyticsConsentGranted = false;
|
|
901
|
+
await _analytics.initialize(token: null, consentGranted: false);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
void _trackAppOpenedIfNeeded({String? consentSource}) {
|
|
906
|
+
if (!_analytics.enabled || _didTrackAppOpen) {
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
_didTrackAppOpen = true;
|
|
910
|
+
unawaited(
|
|
911
|
+
_analytics.trackAppOpened(
|
|
912
|
+
appMode: appMode.name,
|
|
913
|
+
platform: _analyticsPlatformLabel(),
|
|
914
|
+
backendMode: backendUrl.trim().isEmpty ? 'same_origin' : 'custom',
|
|
915
|
+
selectedSection: selectedSection.label,
|
|
916
|
+
deploymentProfile: deploymentProfile,
|
|
917
|
+
authenticated: isAuthenticated,
|
|
918
|
+
),
|
|
919
|
+
);
|
|
920
|
+
if (consentSource != null) {
|
|
921
|
+
unawaited(
|
|
922
|
+
_analytics.track(
|
|
923
|
+
'analytics_consent_granted',
|
|
924
|
+
properties: <String, Object?>{'consent_source': consentSource},
|
|
925
|
+
),
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
String _analyticsPlatformLabel() {
|
|
931
|
+
if (kIsWeb) {
|
|
932
|
+
return 'web';
|
|
933
|
+
}
|
|
934
|
+
switch (defaultTargetPlatform) {
|
|
935
|
+
case TargetPlatform.android:
|
|
936
|
+
return 'android';
|
|
937
|
+
case TargetPlatform.iOS:
|
|
938
|
+
return 'ios';
|
|
939
|
+
case TargetPlatform.macOS:
|
|
940
|
+
return 'macos';
|
|
941
|
+
case TargetPlatform.windows:
|
|
942
|
+
return 'windows';
|
|
943
|
+
case TargetPlatform.linux:
|
|
944
|
+
return 'linux';
|
|
945
|
+
case TargetPlatform.fuchsia:
|
|
946
|
+
return 'fuchsia';
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
Future<void> acceptAnalyticsConsent() async {
|
|
951
|
+
if (!_analyticsConfigured) {
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
_analyticsConsentResolved = true;
|
|
955
|
+
_analyticsConsentGranted = true;
|
|
956
|
+
await _prefs?.setBool('analytics.cookieConsent', true);
|
|
957
|
+
await _analytics.setConsentGranted(true);
|
|
958
|
+
_trackAppOpenedIfNeeded(consentSource: 'banner');
|
|
959
|
+
notifyListeners();
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
Future<void> declineAnalyticsConsent() async {
|
|
963
|
+
if (!_analyticsConfigured) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
_analyticsConsentResolved = true;
|
|
967
|
+
_analyticsConsentGranted = false;
|
|
968
|
+
await _prefs?.setBool('analytics.cookieConsent', false);
|
|
969
|
+
await _analytics.setConsentGranted(false);
|
|
970
|
+
notifyListeners();
|
|
971
|
+
}
|
|
972
|
+
|
|
865
973
|
Future<void> setAppUpdateChannel(String channel) async {
|
|
866
974
|
final normalized = channel.trim().toLowerCase() == 'beta'
|
|
867
975
|
? 'beta'
|
|
@@ -886,6 +994,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
886
994
|
if (isCheckingAppUpdate) {
|
|
887
995
|
return;
|
|
888
996
|
}
|
|
997
|
+
unawaited(_analytics.trackAppUpdateCheck(silent: silent));
|
|
889
998
|
if (!appUpdaterConfigured) {
|
|
890
999
|
appUpdateErrorMessage = kIsWeb
|
|
891
1000
|
? 'Client app update checks are unavailable in the web app.'
|
|
@@ -975,6 +1084,11 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
975
1084
|
isBooting = true;
|
|
976
1085
|
notifyListeners();
|
|
977
1086
|
await bootstrap();
|
|
1087
|
+
unawaited(
|
|
1088
|
+
_analytics.trackBackendUrlSaved(
|
|
1089
|
+
backendMode: backendUrl.trim().isEmpty ? 'same_origin' : 'custom',
|
|
1090
|
+
),
|
|
1091
|
+
);
|
|
978
1092
|
return true;
|
|
979
1093
|
} catch (error) {
|
|
980
1094
|
errorMessage = _friendlyErrorMessage(error);
|
|
@@ -1147,6 +1261,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1147
1261
|
response,
|
|
1148
1262
|
retentionErrorMessage:
|
|
1149
1263
|
'QR login completed, but NeoAgent could not keep the session. Please try again.',
|
|
1264
|
+
authMethod: 'qr',
|
|
1150
1265
|
);
|
|
1151
1266
|
} catch (error) {
|
|
1152
1267
|
final message = _friendlyErrorMessage(error);
|
|
@@ -1199,6 +1314,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1199
1314
|
String? fallbackUsername,
|
|
1200
1315
|
String? retentionErrorMessage,
|
|
1201
1316
|
bool isRegistration = false,
|
|
1317
|
+
String authMethod = 'password',
|
|
1202
1318
|
}) async {
|
|
1203
1319
|
user = Map<String, dynamic>.from(
|
|
1204
1320
|
response['user'] as Map<dynamic, dynamic>? ??
|
|
@@ -1220,6 +1336,14 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1220
1336
|
_clearQrLoginChallenge();
|
|
1221
1337
|
await _persistCredentials();
|
|
1222
1338
|
await refresh();
|
|
1339
|
+
if (isAuthenticated) {
|
|
1340
|
+
unawaited(
|
|
1341
|
+
_analytics.trackSignedIn(
|
|
1342
|
+
authMethod: authMethod,
|
|
1343
|
+
isRegistration: isRegistration,
|
|
1344
|
+
),
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1223
1347
|
if (!isAuthenticated && retentionErrorMessage != null) {
|
|
1224
1348
|
errorMessage = retentionErrorMessage;
|
|
1225
1349
|
}
|
|
@@ -1289,6 +1413,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1289
1413
|
await _completeAuthenticatedResponse(
|
|
1290
1414
|
response,
|
|
1291
1415
|
isRegistration: register,
|
|
1416
|
+
authMethod: 'oauth',
|
|
1292
1417
|
retentionErrorMessage:
|
|
1293
1418
|
'Sign-in completed, but NeoAgent could not keep the browser session. Please sign in again. If this keeps happening, check backend session cookie settings.',
|
|
1294
1419
|
);
|
|
@@ -1315,6 +1440,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1315
1440
|
await _completeAuthenticatedResponse(
|
|
1316
1441
|
response,
|
|
1317
1442
|
fallbackUsername: pendingTwoFactorUsername,
|
|
1443
|
+
authMethod: 'two_factor',
|
|
1318
1444
|
retentionErrorMessage:
|
|
1319
1445
|
'Two-factor sign-in completed, but NeoAgent could not keep the browser session. Please sign in again.',
|
|
1320
1446
|
);
|
|
@@ -1405,6 +1531,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1405
1531
|
response,
|
|
1406
1532
|
fallbackUsername: username,
|
|
1407
1533
|
isRegistration: register,
|
|
1534
|
+
authMethod: 'password',
|
|
1408
1535
|
retentionErrorMessage:
|
|
1409
1536
|
'Sign-in completed, but NeoAgent could not keep the browser session. Please sign in again. If this keeps happening, the backend session cookie is likely not being retained.',
|
|
1410
1537
|
);
|
|
@@ -1435,6 +1562,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1435
1562
|
final logoutFuture = _backendClient.logout(backendUrl);
|
|
1436
1563
|
_authCycle += 1;
|
|
1437
1564
|
_clearAuthenticatedState();
|
|
1565
|
+
unawaited(_analytics.trackSignedOut());
|
|
1438
1566
|
isAuthenticating = true;
|
|
1439
1567
|
notifyListeners();
|
|
1440
1568
|
|
|
@@ -1451,6 +1579,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1451
1579
|
notifyListeners();
|
|
1452
1580
|
try {
|
|
1453
1581
|
await _backendClient.completeOnboarding(backendUrl);
|
|
1582
|
+
unawaited(_analytics.trackOnboardingDismissed());
|
|
1454
1583
|
if (isAuthenticated && user != null) {
|
|
1455
1584
|
user!['hasCompletedOnboarding'] = true;
|
|
1456
1585
|
}
|
|
@@ -1604,6 +1733,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1604
1733
|
}
|
|
1605
1734
|
|
|
1606
1735
|
void setSelectedSection(AppSection section) {
|
|
1736
|
+
final previousSection = selectedSection;
|
|
1607
1737
|
selectedSection = section;
|
|
1608
1738
|
unawaited(_prefs?.setString(_selectedSectionPrefsKey, section.name));
|
|
1609
1739
|
if (section == AppSection.devices) {
|
|
@@ -1612,6 +1742,14 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1612
1742
|
if (section == AppSection.accountSettings) {
|
|
1613
1743
|
unawaited(refreshAccountSettings());
|
|
1614
1744
|
}
|
|
1745
|
+
if (previousSection != section) {
|
|
1746
|
+
unawaited(
|
|
1747
|
+
_analytics.trackSectionChanged(
|
|
1748
|
+
section: section.label,
|
|
1749
|
+
previousSection: previousSection.label,
|
|
1750
|
+
),
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1615
1753
|
notifyListeners();
|
|
1616
1754
|
}
|
|
1617
1755
|
|
|
@@ -2422,6 +2560,9 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
2422
2560
|
}
|
|
2423
2561
|
|
|
2424
2562
|
Future<void> refreshWidgets({bool all = false}) async {
|
|
2563
|
+
if (all) {
|
|
2564
|
+
unawaited(_analytics.trackWidgetRefreshRequested(all: all));
|
|
2565
|
+
}
|
|
2425
2566
|
widgets = _decodeModelList(
|
|
2426
2567
|
'widgets',
|
|
2427
2568
|
await _backendClient.fetchWidgets(
|
|
@@ -3224,6 +3365,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
3224
3365
|
}
|
|
3225
3366
|
|
|
3226
3367
|
Future<void> startBackgroundRecording() async {
|
|
3368
|
+
unawaited(_analytics.trackRecordingStarted(kind: 'background'));
|
|
3227
3369
|
await _startRecordingCapture(
|
|
3228
3370
|
logKey: 'start_background',
|
|
3229
3371
|
payload: buildAndroidBackgroundRecordingPayload(),
|
|
@@ -3250,6 +3392,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
3250
3392
|
}
|
|
3251
3393
|
return;
|
|
3252
3394
|
}
|
|
3395
|
+
unawaited(_analytics.trackRecordingStarted(kind: 'desktop'));
|
|
3253
3396
|
await _startRecordingCapture(
|
|
3254
3397
|
logKey: 'start_desktop',
|
|
3255
3398
|
payload: buildDesktopRecordingPayload(),
|
|
@@ -3633,6 +3776,16 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
3633
3776
|
notifyListeners();
|
|
3634
3777
|
try {
|
|
3635
3778
|
_desktopFloatingToolbarPopupRequested = false;
|
|
3779
|
+
unawaited(
|
|
3780
|
+
_analytics.trackRecordingStopped(
|
|
3781
|
+
kind:
|
|
3782
|
+
recordingRuntime.supportsBackgroundMic &&
|
|
3783
|
+
!recordingRuntime.supportsScreenAndMic
|
|
3784
|
+
? 'background'
|
|
3785
|
+
: 'desktop',
|
|
3786
|
+
stopReason: stopReason,
|
|
3787
|
+
),
|
|
3788
|
+
);
|
|
3636
3789
|
await _recordingBridge.stopActiveRecording();
|
|
3637
3790
|
if (isAndroidBackgroundStop) {
|
|
3638
3791
|
await Future<void>.delayed(const Duration(milliseconds: 600));
|
|
@@ -4261,6 +4414,12 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
4261
4414
|
if (trimmed.isEmpty || (isSendingMessage && !canSteerLiveRun)) {
|
|
4262
4415
|
return;
|
|
4263
4416
|
}
|
|
4417
|
+
unawaited(
|
|
4418
|
+
_analytics.trackChatMessageSent(
|
|
4419
|
+
length: trimmed.length,
|
|
4420
|
+
steeringLiveRun: canSteerLiveRun,
|
|
4421
|
+
),
|
|
4422
|
+
);
|
|
4264
4423
|
|
|
4265
4424
|
final optimistic = ChatEntry(
|
|
4266
4425
|
id: '',
|
|
@@ -4682,6 +4841,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
4682
4841
|
errorMessage = null;
|
|
4683
4842
|
notifyListeners();
|
|
4684
4843
|
try {
|
|
4844
|
+
unawaited(_analytics.trackAppUpdateTriggered());
|
|
4685
4845
|
await _backendClient.triggerUpdate(backendUrl);
|
|
4686
4846
|
await refreshUpdateStatus();
|
|
4687
4847
|
} catch (error) {
|
|
@@ -5412,8 +5572,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
5412
5572
|
return;
|
|
5413
5573
|
}
|
|
5414
5574
|
_pendingChatDraft = normalized;
|
|
5415
|
-
|
|
5416
|
-
notifyListeners();
|
|
5575
|
+
setSelectedSection(AppSection.chat);
|
|
5417
5576
|
}
|
|
5418
5577
|
|
|
5419
5578
|
String? takePendingChatDraft() {
|
|
@@ -5432,17 +5591,15 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
5432
5591
|
|
|
5433
5592
|
void openWidgetSurface(String widgetId) {
|
|
5434
5593
|
final normalized = widgetId.trim();
|
|
5435
|
-
|
|
5594
|
+
setSelectedSection(AppSection.widgets);
|
|
5436
5595
|
if (normalized.isNotEmpty) {
|
|
5437
5596
|
_selectedWidgetId = normalized;
|
|
5438
5597
|
unawaited(refreshWidgets(all: true));
|
|
5439
5598
|
}
|
|
5440
|
-
notifyListeners();
|
|
5441
5599
|
}
|
|
5442
5600
|
|
|
5443
5601
|
void openVoiceAssistantSurface() {
|
|
5444
|
-
|
|
5445
|
-
notifyListeners();
|
|
5602
|
+
setSelectedSection(AppSection.voiceAssistant);
|
|
5446
5603
|
}
|
|
5447
5604
|
|
|
5448
5605
|
String? get selectedWidgetId => _selectedWidgetId;
|
|
@@ -5475,6 +5632,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
5475
5632
|
return;
|
|
5476
5633
|
}
|
|
5477
5634
|
_startManualRunCooldown('task', '$id');
|
|
5635
|
+
unawaited(_analytics.trackTaskRunRequested(taskId: id));
|
|
5478
5636
|
await _backendClient.runSavedTask(backendUrl, id);
|
|
5479
5637
|
await refreshTasks();
|
|
5480
5638
|
await refreshRunsOnly();
|
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
part of 'main.dart';
|
|
2
2
|
|
|
3
3
|
class IntegrationsPanel extends StatelessWidget {
|
|
4
|
-
const IntegrationsPanel({
|
|
4
|
+
const IntegrationsPanel({
|
|
5
|
+
super.key,
|
|
6
|
+
required this.controller,
|
|
7
|
+
this.embedded = false,
|
|
8
|
+
});
|
|
5
9
|
|
|
6
10
|
final NeoAgentController controller;
|
|
11
|
+
final bool embedded;
|
|
7
12
|
|
|
8
13
|
@override
|
|
9
14
|
Widget build(BuildContext context) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
children: <Widget>[
|
|
15
|
+
final body = Column(
|
|
16
|
+
children: <Widget>[
|
|
17
|
+
if (!embedded) ...<Widget>[
|
|
14
18
|
_PageTitle(
|
|
15
19
|
title: 'Integrations',
|
|
16
20
|
subtitle:
|
|
17
21
|
'Connect and manage official integrations separately from reusable skills.',
|
|
18
22
|
),
|
|
19
23
|
const SizedBox(height: 12),
|
|
20
|
-
Expanded(child: OfficialIntegrationsTab(controller: controller)),
|
|
21
24
|
],
|
|
22
|
-
|
|
25
|
+
Expanded(child: OfficialIntegrationsTab(controller: controller)),
|
|
26
|
+
],
|
|
23
27
|
);
|
|
28
|
+
if (embedded) {
|
|
29
|
+
return body;
|
|
30
|
+
}
|
|
31
|
+
return Padding(padding: _pagePadding(context), child: body);
|
|
24
32
|
}
|
|
25
33
|
}
|
|
26
34
|
|
|
@@ -832,7 +832,7 @@ class MessagingAccessCapabilities {
|
|
|
832
832
|
});
|
|
833
833
|
|
|
834
834
|
factory MessagingAccessCapabilities.fromJson(Map<String, dynamic> json) {
|
|
835
|
-
List<String>
|
|
835
|
+
List<String> stringList(dynamic value) {
|
|
836
836
|
if (value is! List) return const <String>[];
|
|
837
837
|
return value
|
|
838
838
|
.map((item) => item.toString())
|
|
@@ -845,9 +845,9 @@ class MessagingAccessCapabilities {
|
|
|
845
845
|
supportsSharedPolicy: json['supportsSharedPolicy'] != false,
|
|
846
846
|
supportsMentionGate: json['supportsMentionGate'] == true,
|
|
847
847
|
supportsDiscovery: json['supportsDiscovery'] == true,
|
|
848
|
-
directRuleScopes:
|
|
849
|
-
sharedSpaceRuleScopes:
|
|
850
|
-
sharedActorRuleScopes:
|
|
848
|
+
directRuleScopes: stringList(json['directRuleScopes']),
|
|
849
|
+
sharedSpaceRuleScopes: stringList(json['sharedSpaceRuleScopes']),
|
|
850
|
+
sharedActorRuleScopes: stringList(json['sharedActorRuleScopes']),
|
|
851
851
|
manualEntryHint: json['manualEntryHint']?.toString() ?? '',
|
|
852
852
|
);
|
|
853
853
|
}
|
|
@@ -932,7 +932,7 @@ class MessagingAccessCatalog {
|
|
|
932
932
|
String platform,
|
|
933
933
|
Map<String, dynamic> json,
|
|
934
934
|
) {
|
|
935
|
-
List<MessagingAccessTarget>
|
|
935
|
+
List<MessagingAccessTarget> parseTargets(dynamic raw) {
|
|
936
936
|
if (raw is! List) return const <MessagingAccessTarget>[];
|
|
937
937
|
return raw
|
|
938
938
|
.whereType<Map>()
|
|
@@ -950,8 +950,8 @@ class MessagingAccessCatalog {
|
|
|
950
950
|
capabilities: MessagingAccessCapabilities.fromJson(
|
|
951
951
|
_jsonMap(json['capabilities']),
|
|
952
952
|
),
|
|
953
|
-
discoveredTargets:
|
|
954
|
-
suggestedTargets:
|
|
953
|
+
discoveredTargets: parseTargets(json['discoveredTargets']),
|
|
954
|
+
suggestedTargets: parseTargets(json['suggestedTargets']),
|
|
955
955
|
summary: json['summary']?.toString() ?? 'Access policy',
|
|
956
956
|
);
|
|
957
957
|
}
|
|
@@ -1434,6 +1434,7 @@ class RunDetailSnapshot {
|
|
|
1434
1434
|
const RunDetailSnapshot({
|
|
1435
1435
|
required this.run,
|
|
1436
1436
|
required this.steps,
|
|
1437
|
+
required this.events,
|
|
1437
1438
|
required this.response,
|
|
1438
1439
|
});
|
|
1439
1440
|
|
|
@@ -1444,12 +1445,17 @@ class RunDetailSnapshot {
|
|
|
1444
1445
|
json['steps'],
|
|
1445
1446
|
fallbackToMapValues: true,
|
|
1446
1447
|
).map(RunStepItem.fromJson).toList(),
|
|
1448
|
+
events: _jsonMapList(
|
|
1449
|
+
json['events'],
|
|
1450
|
+
fallbackToMapValues: true,
|
|
1451
|
+
).map(RunEventItem.fromJson).toList(),
|
|
1447
1452
|
response: json['response']?.toString() ?? '',
|
|
1448
1453
|
);
|
|
1449
1454
|
}
|
|
1450
1455
|
|
|
1451
1456
|
final RunSummary run;
|
|
1452
1457
|
final List<RunStepItem> steps;
|
|
1458
|
+
final List<RunEventItem> events;
|
|
1453
1459
|
final String response;
|
|
1454
1460
|
|
|
1455
1461
|
int get completedTools => steps
|
|
@@ -1469,6 +1475,95 @@ class RunDetailSnapshot {
|
|
|
1469
1475
|
steps.where((step) => step.isPlanningRelated).length;
|
|
1470
1476
|
}
|
|
1471
1477
|
|
|
1478
|
+
class RunEventItem {
|
|
1479
|
+
const RunEventItem({
|
|
1480
|
+
required this.id,
|
|
1481
|
+
required this.eventType,
|
|
1482
|
+
required this.sequenceIndex,
|
|
1483
|
+
required this.requestId,
|
|
1484
|
+
required this.stepId,
|
|
1485
|
+
required this.payload,
|
|
1486
|
+
required this.createdAt,
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
factory RunEventItem.fromJson(Map<dynamic, dynamic> json) {
|
|
1490
|
+
return RunEventItem(
|
|
1491
|
+
id: _asInt(json['id']),
|
|
1492
|
+
eventType:
|
|
1493
|
+
json['eventType']?.toString().ifEmpty(
|
|
1494
|
+
json['event_type']?.toString() ?? 'event',
|
|
1495
|
+
) ??
|
|
1496
|
+
'event',
|
|
1497
|
+
sequenceIndex: _asInt(json['sequenceIndex'] ?? json['sequence_index']),
|
|
1498
|
+
requestId:
|
|
1499
|
+
json['requestId']?.toString() ?? json['request_id']?.toString(),
|
|
1500
|
+
stepId: json['stepId']?.toString() ?? json['step_id']?.toString(),
|
|
1501
|
+
payload: json['payload'] is Map
|
|
1502
|
+
? Map<String, dynamic>.from(json['payload'] as Map)
|
|
1503
|
+
: (json['payload_json'] is Map
|
|
1504
|
+
? Map<String, dynamic>.from(json['payload_json'] as Map)
|
|
1505
|
+
: const <String, dynamic>{}),
|
|
1506
|
+
createdAt: _parseOptionalTimestamp(
|
|
1507
|
+
json['createdAt']?.toString() ?? json['created_at']?.toString(),
|
|
1508
|
+
),
|
|
1509
|
+
);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
final int id;
|
|
1513
|
+
final String eventType;
|
|
1514
|
+
final int sequenceIndex;
|
|
1515
|
+
final String? requestId;
|
|
1516
|
+
final String? stepId;
|
|
1517
|
+
final Map<String, dynamic> payload;
|
|
1518
|
+
final DateTime? createdAt;
|
|
1519
|
+
|
|
1520
|
+
String get title {
|
|
1521
|
+
switch (eventType) {
|
|
1522
|
+
case 'run_started':
|
|
1523
|
+
return 'Run started';
|
|
1524
|
+
case 'memory_injected':
|
|
1525
|
+
return 'Memory injected';
|
|
1526
|
+
case 'model_turn_started':
|
|
1527
|
+
return 'Model turn started';
|
|
1528
|
+
case 'model_turn_completed':
|
|
1529
|
+
return 'Model turn completed';
|
|
1530
|
+
case 'tool_started':
|
|
1531
|
+
return 'Tool started';
|
|
1532
|
+
case 'tool_completed':
|
|
1533
|
+
return 'Tool completed';
|
|
1534
|
+
case 'tool_failed':
|
|
1535
|
+
return 'Tool failed';
|
|
1536
|
+
case 'run_completed':
|
|
1537
|
+
return 'Run completed';
|
|
1538
|
+
case 'run_failed':
|
|
1539
|
+
return 'Run failed';
|
|
1540
|
+
case 'run_stopped':
|
|
1541
|
+
return 'Run stopped';
|
|
1542
|
+
default:
|
|
1543
|
+
return _titleCase(eventType.replaceAll('_', ' '));
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
String get detail {
|
|
1548
|
+
final toolName = payload['toolName']?.toString() ?? '';
|
|
1549
|
+
if (toolName.trim().isNotEmpty) return toolName;
|
|
1550
|
+
final preview =
|
|
1551
|
+
payload['contentPreview']?.toString() ??
|
|
1552
|
+
payload['recallPreview']?.toString() ??
|
|
1553
|
+
'';
|
|
1554
|
+
if (preview.trim().isNotEmpty) return preview;
|
|
1555
|
+
final error = payload['error']?.toString() ?? '';
|
|
1556
|
+
if (error.trim().isNotEmpty) return error;
|
|
1557
|
+
final titleValue = payload['title']?.toString() ?? '';
|
|
1558
|
+
return titleValue;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
String get createdAtLabel =>
|
|
1562
|
+
createdAt == null ? '' : _formatTimestamp(createdAt!);
|
|
1563
|
+
|
|
1564
|
+
bool get isFailure => eventType == 'tool_failed' || eventType == 'run_failed';
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1472
1567
|
class RunStepItem {
|
|
1473
1568
|
const RunStepItem({
|
|
1474
1569
|
required this.id,
|
|
@@ -2802,6 +2897,9 @@ class AiWidgetItem {
|
|
|
2802
2897
|
required this.userId,
|
|
2803
2898
|
required this.agentId,
|
|
2804
2899
|
required this.name,
|
|
2900
|
+
required this.widgetKind,
|
|
2901
|
+
required this.systemKey,
|
|
2902
|
+
required this.isSystem,
|
|
2805
2903
|
required this.template,
|
|
2806
2904
|
required this.layoutVariant,
|
|
2807
2905
|
required this.definition,
|
|
@@ -2823,6 +2921,14 @@ class AiWidgetItem {
|
|
|
2823
2921
|
userId: _asInt(json['userId'] ?? json['user_id']),
|
|
2824
2922
|
agentId: json['agentId']?.toString() ?? json['agent_id']?.toString(),
|
|
2825
2923
|
name: json['name']?.toString().ifEmpty('Widget') ?? 'Widget',
|
|
2924
|
+
widgetKind:
|
|
2925
|
+
json['widgetKind']?.toString().ifEmpty(
|
|
2926
|
+
json['widget_kind']?.toString() ?? 'custom',
|
|
2927
|
+
) ??
|
|
2928
|
+
'custom',
|
|
2929
|
+
systemKey:
|
|
2930
|
+
json['systemKey']?.toString() ?? json['system_key']?.toString(),
|
|
2931
|
+
isSystem: json['isSystem'] == true || json['is_system'] == true,
|
|
2826
2932
|
template: json['template']?.toString().ifEmpty('summary') ?? 'summary',
|
|
2827
2933
|
layoutVariant:
|
|
2828
2934
|
json['layoutVariant']?.toString().ifEmpty(
|
|
@@ -2876,6 +2982,9 @@ class AiWidgetItem {
|
|
|
2876
2982
|
final int userId;
|
|
2877
2983
|
final String? agentId;
|
|
2878
2984
|
final String name;
|
|
2985
|
+
final String widgetKind;
|
|
2986
|
+
final String? systemKey;
|
|
2987
|
+
final bool isSystem;
|
|
2879
2988
|
final String template;
|
|
2880
2989
|
final String layoutVariant;
|
|
2881
2990
|
final Map<String, dynamic> definition;
|