neoagent 2.4.4-beta.0 → 2.4.4-beta.11
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 +17 -0
- package/README.md +9 -3
- package/docs/capabilities.md +16 -7
- package/docs/configuration.md +1 -0
- package/docs/getting-started.md +6 -0
- package/docs/index.md +1 -0
- package/docs/security-boundaries.md +122 -0
- package/docs/supermemory-memory-review.md +852 -0
- package/flutter_app/lib/features/memory/views/retrieval_inspector_view.dart +128 -0
- package/flutter_app/lib/main.dart +3 -0
- package/flutter_app/lib/main_account_settings.dart +79 -15
- package/flutter_app/lib/main_app_shell.dart +22 -0
- package/flutter_app/lib/main_chat.dart +155 -8
- package/flutter_app/lib/main_controller.dart +74 -5
- package/flutter_app/lib/main_devices.dart +9 -3
- package/flutter_app/lib/main_models.dart +32 -0
- package/flutter_app/lib/main_operations.dart +13 -0
- package/flutter_app/lib/main_security.dart +967 -0
- package/flutter_app/lib/main_settings.dart +56 -0
- package/flutter_app/lib/src/backend_client.dart +60 -3
- package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +2 -0
- package/flutter_app/pubspec.lock +32 -0
- package/flutter_app/pubspec.yaml +1 -0
- package/lib/install_helpers.js +1 -0
- package/lib/manager.js +63 -1
- package/lib/schema_migrations.js +262 -0
- package/package.json +4 -2
- package/server/admin/admin.js +151 -0
- package/server/admin/index.html +55 -3
- package/server/db/database.js +3 -0
- package/server/http/routes.js +2 -1
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/NOTICES +86 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +82243 -80308
- package/server/routes/account.js +2 -23
- package/server/routes/admin.js +18 -2
- package/server/routes/agents.js +5 -1
- package/server/routes/memory.js +41 -4
- package/server/routes/security.js +112 -0
- package/server/services/account/service_email_settings.js +167 -0
- package/server/services/ai/engine.js +269 -27
- package/server/services/ai/rate_limits.js +150 -0
- package/server/services/ai/systemPrompt.js +26 -3
- package/server/services/ai/tools.js +11 -8
- package/server/services/cli/shell_worker.js +135 -0
- package/server/services/cli/shell_worker_pool.js +125 -0
- package/server/services/integrations/google/gmail.js +7 -7
- package/server/services/manager.js +20 -1
- package/server/services/memory/consolidation.js +111 -0
- package/server/services/memory/embedding_index.js +175 -0
- package/server/services/memory/embeddings.js +22 -2
- package/server/services/memory/evaluation.js +187 -0
- package/server/services/memory/ingestion_chunking.js +191 -0
- package/server/services/memory/ingestion_documents.js +96 -26
- package/server/services/memory/intelligence.js +3 -1
- package/server/services/memory/manager.js +855 -43
- package/server/services/memory/policy.js +0 -40
- package/server/services/memory/retrieval_reasoning.js +191 -0
- package/server/services/runtime/manager.js +7 -0
- package/server/services/security/approval_gate_service.js +93 -0
- package/server/services/security/tool_categories.js +105 -0
- package/server/services/security/tool_policy_service.js +92 -0
- package/server/services/security/tool_security_hook.js +77 -0
- package/server/services/websocket.js +5 -1
|
@@ -156,6 +156,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
156
156
|
Map<String, MessagingAccessCatalog> messagingAccessCatalogs =
|
|
157
157
|
const <String, MessagingAccessCatalog>{};
|
|
158
158
|
MessagingQrState? pendingMessagingQr;
|
|
159
|
+
ToolApprovalRequest? pendingApproval;
|
|
159
160
|
final List<BlockedSenderNotice> _blockedSenderQueue = <BlockedSenderNotice>[];
|
|
160
161
|
List<SkillItem> skills = const <SkillItem>[];
|
|
161
162
|
List<StoreSkillItem> storeSkills = const <StoreSkillItem>[];
|
|
@@ -359,6 +360,13 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
359
360
|
|
|
360
361
|
String? get sessionCookie => _backendClient.sessionCookie;
|
|
361
362
|
|
|
363
|
+
BackendClient get backendClient => _backendClient;
|
|
364
|
+
|
|
365
|
+
void clearPendingApproval() {
|
|
366
|
+
pendingApproval = null;
|
|
367
|
+
notifyListeners();
|
|
368
|
+
}
|
|
369
|
+
|
|
362
370
|
bool get desktopFloatingToolbarPopupRequested =>
|
|
363
371
|
_desktopFloatingToolbarPopupRequested;
|
|
364
372
|
|
|
@@ -2025,6 +2033,11 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
2025
2033
|
_backendClient.fetchTokenUsageSummary(backendUrl, agentId: agentId),
|
|
2026
2034
|
const <String, dynamic>{},
|
|
2027
2035
|
);
|
|
2036
|
+
final rateLimitFuture = _softRefreshLoad<Map<String, dynamic>>(
|
|
2037
|
+
'rate_limits',
|
|
2038
|
+
_backendClient.fetchAccountUsage(backendUrl),
|
|
2039
|
+
const <String, dynamic>{},
|
|
2040
|
+
);
|
|
2028
2041
|
final updateFuture = _backendClient
|
|
2029
2042
|
.fetchUpdateStatus(backendUrl)
|
|
2030
2043
|
.catchError((_) => const <String, dynamic>{});
|
|
@@ -2134,6 +2147,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
2134
2147
|
final runsResponse = await runsFuture;
|
|
2135
2148
|
final versionResponse = await versionFuture;
|
|
2136
2149
|
final tokenResponse = await tokenFuture;
|
|
2150
|
+
final rateLimitResponse = await rateLimitFuture;
|
|
2137
2151
|
final updateResponse = await updateFuture;
|
|
2138
2152
|
final messagingResponse = await messagingFuture;
|
|
2139
2153
|
final messagingMessagesResponse = await messagingMessagesFuture;
|
|
@@ -2185,6 +2199,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
2185
2199
|
versionInfo = versionResponse;
|
|
2186
2200
|
backendHealthStatus = healthResponse;
|
|
2187
2201
|
tokenUsage = TokenUsageSnapshot.fromJson(tokenResponse);
|
|
2202
|
+
usageAndLimits = AccountUsageAndLimits.fromJson(rateLimitResponse);
|
|
2188
2203
|
updateStatus = UpdateStatusSnapshot.fromJson(updateResponse);
|
|
2189
2204
|
messagingStatuses = messagingResponse.map(
|
|
2190
2205
|
(key, value) => MapEntry(
|
|
@@ -4632,20 +4647,24 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
4632
4647
|
}
|
|
4633
4648
|
activeRun = null;
|
|
4634
4649
|
await refreshRunsOnly();
|
|
4650
|
+
await refreshRateLimitUsage();
|
|
4635
4651
|
} catch (error) {
|
|
4652
|
+
final friendlyError = _friendlyErrorMessage(error);
|
|
4636
4653
|
chatMessages = <ChatEntry>[
|
|
4637
4654
|
...chatMessages,
|
|
4638
4655
|
ChatEntry(
|
|
4639
4656
|
id: '',
|
|
4640
4657
|
role: 'assistant',
|
|
4641
|
-
content:
|
|
4642
|
-
'I could not complete that request right now. Please try again in a moment.',
|
|
4658
|
+
content: friendlyError,
|
|
4643
4659
|
platform: 'flutter',
|
|
4644
4660
|
createdAt: DateTime.now(),
|
|
4645
4661
|
),
|
|
4646
4662
|
];
|
|
4647
4663
|
activeRun = null;
|
|
4648
|
-
errorMessage =
|
|
4664
|
+
errorMessage = friendlyError;
|
|
4665
|
+
if (error is BackendException && error.statusCode == 429) {
|
|
4666
|
+
await refreshRateLimitUsage();
|
|
4667
|
+
}
|
|
4649
4668
|
} finally {
|
|
4650
4669
|
if (_socket == null || !socketConnected) {
|
|
4651
4670
|
isSendingMessage = false;
|
|
@@ -4778,6 +4797,16 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
4778
4797
|
}
|
|
4779
4798
|
}
|
|
4780
4799
|
|
|
4800
|
+
Future<void> refreshRateLimitUsage() async {
|
|
4801
|
+
if (!isAuthenticated) return;
|
|
4802
|
+
try {
|
|
4803
|
+
usageAndLimits = AccountUsageAndLimits.fromJson(
|
|
4804
|
+
await _backendClient.fetchAccountUsage(backendUrl),
|
|
4805
|
+
);
|
|
4806
|
+
notifyListeners();
|
|
4807
|
+
} catch (_) {}
|
|
4808
|
+
}
|
|
4809
|
+
|
|
4781
4810
|
Future<bool> updateAccountEmail({
|
|
4782
4811
|
required String email,
|
|
4783
4812
|
required String currentPassword,
|
|
@@ -5618,7 +5647,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
5618
5647
|
}
|
|
5619
5648
|
|
|
5620
5649
|
Future<void> searchMemories(String query) async {
|
|
5621
|
-
memoryRecallResults = (await _backendClient.
|
|
5650
|
+
memoryRecallResults = (await _backendClient.recallMemory(
|
|
5622
5651
|
backendUrl,
|
|
5623
5652
|
query,
|
|
5624
5653
|
agentId: _scopedAgentId,
|
|
@@ -5631,6 +5660,14 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
5631
5660
|
notifyListeners();
|
|
5632
5661
|
}
|
|
5633
5662
|
|
|
5663
|
+
Future<Map<String, dynamic>> inspectMemory(String query) async {
|
|
5664
|
+
return _backendClient.inspectMemory(
|
|
5665
|
+
backendUrl,
|
|
5666
|
+
query,
|
|
5667
|
+
agentId: _scopedAgentId,
|
|
5668
|
+
);
|
|
5669
|
+
}
|
|
5670
|
+
|
|
5634
5671
|
Future<void> updateAssistantBehaviorNotes(String content) async {
|
|
5635
5672
|
await _backendClient.saveSettings(backendUrl, <String, dynamic>{
|
|
5636
5673
|
'assistant_behavior_notes': content,
|
|
@@ -7146,6 +7183,25 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
7146
7183
|
}
|
|
7147
7184
|
notifyListeners();
|
|
7148
7185
|
});
|
|
7186
|
+
socket.on('tool:approval_required', (dynamic data) {
|
|
7187
|
+
final payload = _jsonMap(data);
|
|
7188
|
+
final req = ToolApprovalRequest.fromJson(payload);
|
|
7189
|
+
pendingApproval = req;
|
|
7190
|
+
notifyListeners();
|
|
7191
|
+
// Show interactive push notification when app is backgrounded
|
|
7192
|
+
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
|
7193
|
+
_SecurityNotificationService.showApprovalNotification(req);
|
|
7194
|
+
}
|
|
7195
|
+
});
|
|
7196
|
+
socket.on('tool:approval_resolved', (dynamic data) {
|
|
7197
|
+
final payload = _jsonMap(data);
|
|
7198
|
+
final resolvedId = payload['approvalId']?.toString() ?? '';
|
|
7199
|
+
if (pendingApproval?.approvalId == resolvedId) {
|
|
7200
|
+
_SecurityNotificationService.cancelApprovalNotification(resolvedId);
|
|
7201
|
+
pendingApproval = null;
|
|
7202
|
+
notifyListeners();
|
|
7203
|
+
}
|
|
7204
|
+
});
|
|
7149
7205
|
socket.on('run:steer_queued', (dynamic data) {
|
|
7150
7206
|
final payload = _jsonMap(data);
|
|
7151
7207
|
final runId = payload['runId']?.toString() ?? '';
|
|
@@ -7257,11 +7313,13 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
7257
7313
|
final payload = _jsonMap(data);
|
|
7258
7314
|
final runId = payload['runId']?.toString() ?? '';
|
|
7259
7315
|
if (_voiceRunIds.remove(runId)) {
|
|
7316
|
+
unawaited(refreshRateLimitUsage());
|
|
7260
7317
|
return;
|
|
7261
7318
|
}
|
|
7262
7319
|
if (_backgroundRunIds.remove(runId)) {
|
|
7263
7320
|
unawaited(refreshRunsOnly());
|
|
7264
7321
|
unawaited(refreshMemory());
|
|
7322
|
+
unawaited(refreshRateLimitUsage());
|
|
7265
7323
|
notifyListeners();
|
|
7266
7324
|
return;
|
|
7267
7325
|
}
|
|
@@ -7285,6 +7343,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
7285
7343
|
);
|
|
7286
7344
|
}
|
|
7287
7345
|
unawaited(refreshRunsOnly());
|
|
7346
|
+
unawaited(refreshRateLimitUsage());
|
|
7288
7347
|
notifyListeners();
|
|
7289
7348
|
});
|
|
7290
7349
|
socket.on('run:stopped', (dynamic data) {
|
|
@@ -7335,8 +7394,18 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
7335
7394
|
streamingAssistant = '';
|
|
7336
7395
|
activeRun = null;
|
|
7337
7396
|
isSendingMessage = false;
|
|
7338
|
-
|
|
7397
|
+
final message =
|
|
7398
|
+
payload['error']?.toString().trim() ??
|
|
7339
7399
|
'I could not complete that request right now. Please try again in a moment.';
|
|
7400
|
+
errorMessage = _friendlyErrorMessage(
|
|
7401
|
+
BackendException(
|
|
7402
|
+
message,
|
|
7403
|
+
statusCode: payload['code'] == 'RATE_LIMIT_EXCEEDED' ? 429 : null,
|
|
7404
|
+
),
|
|
7405
|
+
);
|
|
7406
|
+
if (payload['code'] == 'RATE_LIMIT_EXCEEDED') {
|
|
7407
|
+
unawaited(refreshRateLimitUsage());
|
|
7408
|
+
}
|
|
7340
7409
|
notifyListeners();
|
|
7341
7410
|
});
|
|
7342
7411
|
socket.on('tasks:task_complete', (dynamic _) {
|
|
@@ -1734,6 +1734,14 @@ class _InteractiveSurfacePreviewState
|
|
|
1734
1734
|
Widget build(BuildContext context) {
|
|
1735
1735
|
final path = widget.screenshotPath;
|
|
1736
1736
|
final hasImage = path != null && path.isNotEmpty;
|
|
1737
|
+
final viewportHeight = MediaQuery.sizeOf(context).height;
|
|
1738
|
+
final surfaceHeightCap = widget.surface == _DeviceSurface.android
|
|
1739
|
+
? 640.0
|
|
1740
|
+
: 560.0;
|
|
1741
|
+
final previewMaxHeight = math.min(
|
|
1742
|
+
surfaceHeightCap,
|
|
1743
|
+
viewportHeight * 0.48,
|
|
1744
|
+
);
|
|
1737
1745
|
final aspectRatio = switch (widget.surface) {
|
|
1738
1746
|
_DeviceSurface.browser => 16 / 10,
|
|
1739
1747
|
_DeviceSurface.android => 10 / 16,
|
|
@@ -1755,9 +1763,7 @@ class _InteractiveSurfacePreviewState
|
|
|
1755
1763
|
child: Column(
|
|
1756
1764
|
children: <Widget>[
|
|
1757
1765
|
ConstrainedBox(
|
|
1758
|
-
constraints: BoxConstraints(
|
|
1759
|
-
maxHeight: widget.surface == _DeviceSurface.android ? 640 : 560,
|
|
1760
|
-
),
|
|
1766
|
+
constraints: BoxConstraints(maxHeight: previewMaxHeight),
|
|
1761
1767
|
child: AspectRatio(
|
|
1762
1768
|
aspectRatio: aspectRatio,
|
|
1763
1769
|
child: ClipRRect(
|
|
@@ -4111,6 +4111,12 @@ class AccountUsageAndLimits {
|
|
|
4111
4111
|
this.weeklyLimit,
|
|
4112
4112
|
this.fourHourUsage = 0,
|
|
4113
4113
|
this.weeklyUsage = 0,
|
|
4114
|
+
this.fourHourRemaining = 0,
|
|
4115
|
+
this.weeklyRemaining = 0,
|
|
4116
|
+
this.fourHourReached = false,
|
|
4117
|
+
this.weeklyReached = false,
|
|
4118
|
+
this.fourHourNextDecreaseAt,
|
|
4119
|
+
this.weeklyNextDecreaseAt,
|
|
4114
4120
|
this.fourHourIsCustom = false,
|
|
4115
4121
|
this.weeklyIsCustom = false,
|
|
4116
4122
|
});
|
|
@@ -4118,11 +4124,28 @@ class AccountUsageAndLimits {
|
|
|
4118
4124
|
factory AccountUsageAndLimits.fromJson(Map<String, dynamic> json) {
|
|
4119
4125
|
final limits = json['limits'] is Map ? json['limits'] as Map : const {};
|
|
4120
4126
|
final usage = json['usage'] is Map ? json['usage'] as Map : const {};
|
|
4127
|
+
final remaining = json['remaining'] is Map
|
|
4128
|
+
? json['remaining'] as Map
|
|
4129
|
+
: const {};
|
|
4130
|
+
final reached = json['reached'] is Map ? json['reached'] as Map : const {};
|
|
4131
|
+
final nextDecreaseAt = json['nextDecreaseAt'] is Map
|
|
4132
|
+
? json['nextDecreaseAt'] as Map
|
|
4133
|
+
: const {};
|
|
4121
4134
|
return AccountUsageAndLimits(
|
|
4122
4135
|
fourHourLimit: int.tryParse(limits['fourHour']?.toString() ?? ''),
|
|
4123
4136
|
weeklyLimit: int.tryParse(limits['weekly']?.toString() ?? ''),
|
|
4124
4137
|
fourHourUsage: _asInt(usage['fourHour']),
|
|
4125
4138
|
weeklyUsage: _asInt(usage['weekly']),
|
|
4139
|
+
fourHourRemaining: _asInt(remaining['fourHour']),
|
|
4140
|
+
weeklyRemaining: _asInt(remaining['weekly']),
|
|
4141
|
+
fourHourReached: reached['fourHour'] == true,
|
|
4142
|
+
weeklyReached: reached['weekly'] == true,
|
|
4143
|
+
fourHourNextDecreaseAt: DateTime.tryParse(
|
|
4144
|
+
nextDecreaseAt['fourHour']?.toString() ?? '',
|
|
4145
|
+
),
|
|
4146
|
+
weeklyNextDecreaseAt: DateTime.tryParse(
|
|
4147
|
+
nextDecreaseAt['weekly']?.toString() ?? '',
|
|
4148
|
+
),
|
|
4126
4149
|
fourHourIsCustom: limits['fourHourIsCustom'] == true,
|
|
4127
4150
|
weeklyIsCustom: limits['weeklyIsCustom'] == true,
|
|
4128
4151
|
);
|
|
@@ -4132,6 +4155,15 @@ class AccountUsageAndLimits {
|
|
|
4132
4155
|
final int? weeklyLimit;
|
|
4133
4156
|
final int fourHourUsage;
|
|
4134
4157
|
final int weeklyUsage;
|
|
4158
|
+
final int fourHourRemaining;
|
|
4159
|
+
final int weeklyRemaining;
|
|
4160
|
+
final bool fourHourReached;
|
|
4161
|
+
final bool weeklyReached;
|
|
4162
|
+
final DateTime? fourHourNextDecreaseAt;
|
|
4163
|
+
final DateTime? weeklyNextDecreaseAt;
|
|
4135
4164
|
final bool fourHourIsCustom;
|
|
4136
4165
|
final bool weeklyIsCustom;
|
|
4166
|
+
|
|
4167
|
+
bool get hasLimits => fourHourLimit != null || weeklyLimit != null;
|
|
4168
|
+
bool get isReached => fourHourReached || weeklyReached;
|
|
4137
4169
|
}
|
|
@@ -1463,6 +1463,14 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1463
1463
|
});
|
|
1464
1464
|
}
|
|
1465
1465
|
|
|
1466
|
+
void _openRetrievalInspector(BuildContext context, NeoAgentController controller) {
|
|
1467
|
+
Navigator.of(context).push(
|
|
1468
|
+
MaterialPageRoute(
|
|
1469
|
+
builder: (context) => RetrievalInspectorView(controller: controller),
|
|
1470
|
+
),
|
|
1471
|
+
);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1466
1474
|
@override
|
|
1467
1475
|
Widget build(BuildContext context) {
|
|
1468
1476
|
final controller = widget.controller;
|
|
@@ -1485,6 +1493,11 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1485
1493
|
spacing: 10,
|
|
1486
1494
|
runSpacing: 10,
|
|
1487
1495
|
children: <Widget>[
|
|
1496
|
+
OutlinedButton.icon(
|
|
1497
|
+
onPressed: () => _openRetrievalInspector(context, controller),
|
|
1498
|
+
icon: Icon(Icons.bug_report_outlined),
|
|
1499
|
+
label: Text('Inspect'),
|
|
1500
|
+
),
|
|
1488
1501
|
OutlinedButton.icon(
|
|
1489
1502
|
onPressed: () => _openBehaviorNotesEditor(context, controller),
|
|
1490
1503
|
icon: Icon(Icons.edit_outlined),
|