neoagent 3.0.1-beta.22 → 3.0.1-beta.24
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 +0 -8
- package/flutter_app/lib/main.dart +1 -0
- package/flutter_app/lib/main_app_shell.dart +28 -13
- package/flutter_app/lib/main_chat.dart +28 -0
- package/flutter_app/lib/main_controller.dart +145 -12
- package/flutter_app/lib/main_devices.dart +292 -206
- package/flutter_app/lib/main_models.dart +90 -0
- package/flutter_app/lib/main_navigation.dart +13 -1
- package/flutter_app/lib/main_settings.dart +369 -255
- package/flutter_app/lib/main_shared.dart +1 -0
- package/flutter_app/lib/main_timeline.dart +230 -0
- package/flutter_app/lib/src/backend_client.dart +64 -21
- package/flutter_app/lib/src/desktop_companion_actions.dart +132 -21
- package/flutter_app/lib/src/desktop_companion_io.dart +65 -1
- package/flutter_app/lib/src/desktop_companion_stub.dart +12 -0
- package/flutter_app/lib/src/desktop_ocr_bridge.dart +2 -0
- package/flutter_app/lib/src/desktop_ocr_bridge_io.dart +125 -0
- package/flutter_app/lib/src/desktop_ocr_bridge_stub.dart +30 -0
- package/flutter_app/lib/src/desktop_passive_history.dart +332 -0
- package/flutter_app/lib/src/recording_bridge_io.dart +80 -72
- package/flutter_app/lib/src/recording_bridge_web.dart +127 -114
- package/flutter_app/lib/src/recording_chunk_queue.dart +149 -0
- package/flutter_app/lib/src/recording_chunk_queue_io.dart +164 -0
- package/flutter_app/lib/src/recording_payloads.dart +9 -0
- package/flutter_app/macos/Runner/AppDelegate.swift +25 -0
- package/flutter_app/windows/runner/flutter_window.cpp +75 -0
- package/landing/index.html +2 -2
- package/package.json +1 -1
- package/server/admin/admin.js +0 -70
- package/server/admin/index.html +0 -4
- package/server/db/database.js +71 -0
- package/server/http/routes.js +1 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/canvaskit/wimp.js.symbols +8475 -8467
- package/server/public/canvaskit/wimp.wasm +0 -0
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/main.dart.js +72781 -72298
- package/server/routes/admin.js +0 -43
- package/server/routes/recordings.js +96 -6
- package/server/routes/screenHistory.js +140 -2
- package/server/routes/timeline.js +43 -0
- package/server/services/ai/loop/agent_engine_core.js +8 -1
- package/server/services/ai/loop/conversation_loop.js +34 -5
- package/server/services/desktop/auth.js +3 -0
- package/server/services/desktop/registry.js +50 -2
- package/server/services/manager.js +12 -42
- package/server/services/recordings/manager.js +60 -27
- package/server/services/runtime/guest_image.js +6 -0
- package/server/services/tasks/runtime.js +104 -0
- package/server/services/timeline/service.js +521 -0
- package/server/services/desktop/screenRecorder.js +0 -292
- package/server/services/desktop/screen_recorder_support.js +0 -46
package/.env.example
CHANGED
|
@@ -253,14 +253,6 @@ OLLAMA_URL=http://localhost:11434
|
|
|
253
253
|
# Connected integration memory refresh cadence in milliseconds.
|
|
254
254
|
NEOAGENT_MEMORY_INGESTION_INTERVAL_MS=600000
|
|
255
255
|
|
|
256
|
-
# Local screen OCR capture. This captures the backend host's macOS desktop,
|
|
257
|
-
# so enable it only when that host is the configured user's desktop.
|
|
258
|
-
NEOAGENT_SCREEN_RECORDER_ENABLED=false
|
|
259
|
-
# Existing NeoAgent user ID that owns all captured screen history. Required when enabled.
|
|
260
|
-
NEOAGENT_SCREEN_RECORDER_USER_ID=
|
|
261
|
-
NEOAGENT_SCREEN_RECORDER_INTERVAL_MS=10000
|
|
262
|
-
NEOAGENT_SCREEN_RECORDER_RETENTION_DAYS=7
|
|
263
|
-
|
|
264
256
|
########################################
|
|
265
257
|
# Messaging and voice integrations
|
|
266
258
|
########################################
|
|
@@ -205,9 +205,15 @@ class _BackendSetupViewState extends State<BackendSetupView> {
|
|
|
205
205
|
const SizedBox(height: 10),
|
|
206
206
|
Center(
|
|
207
207
|
child: TextButton.icon(
|
|
208
|
-
onPressed: () =>
|
|
209
|
-
|
|
210
|
-
|
|
208
|
+
onPressed: () =>
|
|
209
|
+
setState(() => _localInstall = true),
|
|
210
|
+
icon: const Icon(
|
|
211
|
+
Icons.download_outlined,
|
|
212
|
+
size: 16,
|
|
213
|
+
),
|
|
214
|
+
label: const Text(
|
|
215
|
+
'Install NeoAgent on this machine',
|
|
216
|
+
),
|
|
211
217
|
),
|
|
212
218
|
),
|
|
213
219
|
],
|
|
@@ -268,7 +274,9 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
268
274
|
if (!mounted) return;
|
|
269
275
|
setState(() => _phase = _LocalInstallPhase.checking);
|
|
270
276
|
|
|
271
|
-
final r = await Process.run(Platform.isWindows ? 'where' : 'which', [
|
|
277
|
+
final r = await Process.run(Platform.isWindows ? 'where' : 'which', [
|
|
278
|
+
'node',
|
|
279
|
+
]);
|
|
272
280
|
if (r.exitCode == 0) {
|
|
273
281
|
_nodePath = (r.stdout as String).trim().split('\n').first.trim();
|
|
274
282
|
} else if (!Platform.isWindows) {
|
|
@@ -315,7 +323,8 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
315
323
|
if (!_ensureEnvFile()) {
|
|
316
324
|
setState(() {
|
|
317
325
|
_phase = _LocalInstallPhase.failed;
|
|
318
|
-
_errorMsg =
|
|
326
|
+
_errorMsg =
|
|
327
|
+
'Could not create ~/.neoagent/runtime/.env — check directory permissions.';
|
|
319
328
|
});
|
|
320
329
|
return;
|
|
321
330
|
}
|
|
@@ -324,11 +333,10 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
324
333
|
_log.clear();
|
|
325
334
|
});
|
|
326
335
|
|
|
327
|
-
final process = await Process.start(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
);
|
|
336
|
+
final process = await Process.start(_nodePath!, [
|
|
337
|
+
'bin/neoagent.js',
|
|
338
|
+
'install',
|
|
339
|
+
], workingDirectory: _installDir);
|
|
332
340
|
_proc = process;
|
|
333
341
|
|
|
334
342
|
void onChunk(String data) {
|
|
@@ -349,7 +357,8 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
349
357
|
if (!mounted) return;
|
|
350
358
|
setState(() {
|
|
351
359
|
_phase = exit == 0 ? _LocalInstallPhase.done : _LocalInstallPhase.failed;
|
|
352
|
-
if (exit != 0)
|
|
360
|
+
if (exit != 0)
|
|
361
|
+
_errorMsg = 'Installation failed (exit $exit). Check the log above.';
|
|
353
362
|
});
|
|
354
363
|
}
|
|
355
364
|
|
|
@@ -388,7 +397,10 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
388
397
|
const SizedBox(height: 16),
|
|
389
398
|
const _BrandLockup(logoSize: 60),
|
|
390
399
|
const SizedBox(height: 22),
|
|
391
|
-
Text(
|
|
400
|
+
Text(
|
|
401
|
+
'INSTALL LOCALLY',
|
|
402
|
+
style: _sectionEyebrowStyle(),
|
|
403
|
+
),
|
|
392
404
|
const SizedBox(height: 10),
|
|
393
405
|
Text(
|
|
394
406
|
'Install NeoAgent as a background service',
|
|
@@ -459,7 +471,8 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
459
471
|
final proc = _proc;
|
|
460
472
|
proc?.kill(ProcessSignal.sigterm);
|
|
461
473
|
await proc?.exitCode;
|
|
462
|
-
if (mounted)
|
|
474
|
+
if (mounted)
|
|
475
|
+
setState(() => _phase = _LocalInstallPhase.ready);
|
|
463
476
|
},
|
|
464
477
|
child: const Text('Cancel'),
|
|
465
478
|
),
|
|
@@ -2496,6 +2509,8 @@ class _SectionBody extends StatelessWidget {
|
|
|
2496
2509
|
switch (controller.selectedSection) {
|
|
2497
2510
|
case AppSection.chat:
|
|
2498
2511
|
return ChatPanel(controller: controller);
|
|
2512
|
+
case AppSection.timeline:
|
|
2513
|
+
return TimelinePanel(controller: controller);
|
|
2499
2514
|
case AppSection.voiceAssistant:
|
|
2500
2515
|
return VoiceAssistantPanel(controller: controller);
|
|
2501
2516
|
case AppSection.devices:
|
|
@@ -2453,6 +2453,34 @@ class _RunsPanelState extends State<RunsPanel> {
|
|
|
2453
2453
|
}
|
|
2454
2454
|
|
|
2455
2455
|
void _syncSelection() {
|
|
2456
|
+
final requestedRunId = widget.controller.requestedRunFocusId?.trim();
|
|
2457
|
+
if (requestedRunId != null && requestedRunId.isNotEmpty) {
|
|
2458
|
+
final requestedRunExists = widget.controller.recentRuns.any(
|
|
2459
|
+
(run) => run.id == requestedRunId,
|
|
2460
|
+
);
|
|
2461
|
+
if (_filteredRuns.any((run) => run.id == requestedRunId)) {
|
|
2462
|
+
widget.controller.clearRequestedRunFocus(requestedRunId);
|
|
2463
|
+
if (_selectedRunId != requestedRunId) {
|
|
2464
|
+
unawaited(_selectRun(requestedRunId));
|
|
2465
|
+
}
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
2468
|
+
if (requestedRunExists) {
|
|
2469
|
+
if (_statusFilter != 'all') {
|
|
2470
|
+
if (mounted) {
|
|
2471
|
+
setState(() {
|
|
2472
|
+
_statusFilter = 'all';
|
|
2473
|
+
});
|
|
2474
|
+
} else {
|
|
2475
|
+
_statusFilter = 'all';
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
if (_searchController.text.isNotEmpty) {
|
|
2479
|
+
_searchController.clear();
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2456
2484
|
final runs = _filteredRuns;
|
|
2457
2485
|
if (runs.isEmpty) {
|
|
2458
2486
|
_selectedRunId = null;
|
|
@@ -47,7 +47,8 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
47
47
|
StreamSubscription<List<ConnectivityResult>>? _connectivitySubscription;
|
|
48
48
|
bool _connectivityPluginAvailable = true;
|
|
49
49
|
static const int _maxVisibleLogs = 400;
|
|
50
|
-
static const int _maxToolEvents =
|
|
50
|
+
static const int _maxToolEvents =
|
|
51
|
+
500; // separate list from _maxVisibleLogs (chat diagnostics)
|
|
51
52
|
|
|
52
53
|
static const String _configuredBackendUrl = String.fromEnvironment(
|
|
53
54
|
'NEOAGENT_BACKEND_URL',
|
|
@@ -153,6 +154,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
153
154
|
List<ModelMeta> supportedModels = const <ModelMeta>[];
|
|
154
155
|
List<AiProviderMeta> aiProviders = const <AiProviderMeta>[];
|
|
155
156
|
List<RunSummary> recentRuns = const <RunSummary>[];
|
|
157
|
+
List<TimelineEventItem> timelineItems = const <TimelineEventItem>[];
|
|
156
158
|
TokenUsageSnapshot? tokenUsage;
|
|
157
159
|
Map<String, dynamic>? billingSubscription;
|
|
158
160
|
List<Map<String, dynamic>> billingPlans = const <Map<String, dynamic>>[];
|
|
@@ -212,6 +214,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
212
214
|
String? _chatHistoryBeforeCreatedAt;
|
|
213
215
|
String? _chatHistoryBeforeSource;
|
|
214
216
|
String? _chatHistoryBeforeId;
|
|
217
|
+
String? _requestedRunFocusId;
|
|
215
218
|
|
|
216
219
|
ActiveRunState? activeRun;
|
|
217
220
|
List<ToolEventItem> toolEvents = const <ToolEventItem>[];
|
|
@@ -240,14 +243,23 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
240
243
|
bool _desktopKeepRunningOnClose = true;
|
|
241
244
|
bool _desktopAutoShowFloatingToolbar = true;
|
|
242
245
|
bool _desktopAssistantHotkeyEnabled = true;
|
|
246
|
+
bool isRefreshingTimeline = false;
|
|
247
|
+
Set<String> selectedTimelineSources = <String>{'screen', 'tasks', 'runs'};
|
|
243
248
|
|
|
244
249
|
bool get desktopCompanionEnabled => _desktopCompanion.enabled;
|
|
250
|
+
bool get desktopPassiveHistoryEnabled =>
|
|
251
|
+
_desktopCompanion.passiveHistoryEnabled;
|
|
245
252
|
bool get isLauncherMode => appMode == NeoAgentAppMode.launcher;
|
|
246
253
|
bool get desktopCompanionConnected => _desktopCompanion.connected;
|
|
247
254
|
bool get desktopCompanionConnecting => _desktopCompanion.connecting;
|
|
248
255
|
bool get desktopCompanionPaused => _desktopCompanion.paused;
|
|
249
256
|
String get desktopCompanionLabel => _desktopCompanion.label;
|
|
250
257
|
String? get desktopCompanionErrorMessage => _desktopCompanion.errorMessage;
|
|
258
|
+
String? get desktopPassiveHistoryLastUploadedAt =>
|
|
259
|
+
_desktopCompanion.passiveHistoryLastUploadedAt;
|
|
260
|
+
String? get desktopPassiveHistoryLastError =>
|
|
261
|
+
_desktopCompanion.passiveHistoryLastError;
|
|
262
|
+
String? get requestedRunFocusId => _requestedRunFocusId;
|
|
251
263
|
Map<String, Object?> get desktopCompanionStatus => _desktopCompanion.status;
|
|
252
264
|
|
|
253
265
|
bool get hasLiveRun => isSendingMessage && activeRun != null;
|
|
@@ -828,7 +840,9 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
828
840
|
|
|
829
841
|
Future<void> bootstrap() async {
|
|
830
842
|
_prefs = await SharedPreferences.getInstance();
|
|
831
|
-
_ignoredChats.addAll(
|
|
843
|
+
_ignoredChats.addAll(
|
|
844
|
+
_prefs?.getStringList('messaging.ignored_chats') ?? <String>[],
|
|
845
|
+
);
|
|
832
846
|
await _desktopCompanion.bootstrap(_prefs!);
|
|
833
847
|
final configured = _configuredBackendUrl.trim();
|
|
834
848
|
final savedBackendUrl = _prefs?.getString('backend_url')?.trim() ?? '';
|
|
@@ -1671,6 +1685,8 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1671
1685
|
supportedModels = const <ModelMeta>[];
|
|
1672
1686
|
aiProviders = const <AiProviderMeta>[];
|
|
1673
1687
|
recentRuns = const <RunSummary>[];
|
|
1688
|
+
timelineItems = const <TimelineEventItem>[];
|
|
1689
|
+
isRefreshingTimeline = false;
|
|
1674
1690
|
tokenUsage = null;
|
|
1675
1691
|
updateStatus = const UpdateStatusSnapshot();
|
|
1676
1692
|
_serverLogs = const <LogEntry>[];
|
|
@@ -1794,6 +1810,9 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1794
1810
|
if (section == AppSection.devices) {
|
|
1795
1811
|
unawaited(refreshDevices());
|
|
1796
1812
|
}
|
|
1813
|
+
if (section == AppSection.timeline) {
|
|
1814
|
+
unawaited(refreshTimeline());
|
|
1815
|
+
}
|
|
1797
1816
|
if (section == AppSection.accountSettings) {
|
|
1798
1817
|
unawaited(refreshAccountSettings());
|
|
1799
1818
|
}
|
|
@@ -1803,6 +1822,22 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1803
1822
|
notifyListeners();
|
|
1804
1823
|
}
|
|
1805
1824
|
|
|
1825
|
+
Future<void> openRunDetails(String runId) async {
|
|
1826
|
+
final normalized = runId.trim();
|
|
1827
|
+
if (normalized.isEmpty) {
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
_requestedRunFocusId = normalized;
|
|
1831
|
+
setSelectedSection(AppSection.runs);
|
|
1832
|
+
await refreshRunsOnly();
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
void clearRequestedRunFocus(String runId) {
|
|
1836
|
+
if (_requestedRunFocusId == runId) {
|
|
1837
|
+
_requestedRunFocusId = null;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1806
1841
|
void _ensureSelectedAgent() {
|
|
1807
1842
|
if (agentProfiles.isEmpty) {
|
|
1808
1843
|
selectedAgentId = null;
|
|
@@ -2058,15 +2093,23 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
2058
2093
|
final key = '${notice.platform}:${notice.chatId ?? notice.sender ?? ''}';
|
|
2059
2094
|
_ignoredChats.add(key);
|
|
2060
2095
|
_blockedSenderQueue.removeWhere(
|
|
2061
|
-
(n) =>
|
|
2096
|
+
(n) =>
|
|
2097
|
+
n.platform == notice.platform &&
|
|
2098
|
+
(n.chatId == notice.chatId || n.sender == notice.sender),
|
|
2099
|
+
);
|
|
2100
|
+
await _prefs?.setStringList(
|
|
2101
|
+
'messaging.ignored_chats',
|
|
2102
|
+
_ignoredChats.toList(),
|
|
2062
2103
|
);
|
|
2063
|
-
await _prefs?.setStringList('messaging.ignored_chats', _ignoredChats.toList());
|
|
2064
2104
|
notifyListeners();
|
|
2065
2105
|
}
|
|
2066
2106
|
|
|
2067
2107
|
Future<void> removeIgnoredChat(String key) async {
|
|
2068
2108
|
_ignoredChats.remove(key);
|
|
2069
|
-
await _prefs?.setStringList(
|
|
2109
|
+
await _prefs?.setStringList(
|
|
2110
|
+
'messaging.ignored_chats',
|
|
2111
|
+
_ignoredChats.toList(),
|
|
2112
|
+
);
|
|
2070
2113
|
notifyListeners();
|
|
2071
2114
|
}
|
|
2072
2115
|
|
|
@@ -2080,7 +2123,8 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
2080
2123
|
}
|
|
2081
2124
|
|
|
2082
2125
|
void _enqueueBlockedSenderNotice(BlockedSenderNotice notice) {
|
|
2083
|
-
final ignoreKey =
|
|
2126
|
+
final ignoreKey =
|
|
2127
|
+
'${notice.platform}:${notice.chatId ?? notice.sender ?? ''}';
|
|
2084
2128
|
if (_ignoredChats.contains(ignoreKey)) return;
|
|
2085
2129
|
final exists = _blockedSenderQueue.any((item) => item.id == notice.id);
|
|
2086
2130
|
if (!exists) {
|
|
@@ -2178,6 +2222,15 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
2178
2222
|
_backendClient.fetchRuns(backendUrl, agentId: agentId),
|
|
2179
2223
|
const <String, dynamic>{'runs': <dynamic>[]},
|
|
2180
2224
|
);
|
|
2225
|
+
final timelineFuture = _softRefreshLoad<Map<String, dynamic>>(
|
|
2226
|
+
'timeline',
|
|
2227
|
+
_backendClient.fetchTimeline(
|
|
2228
|
+
backendUrl,
|
|
2229
|
+
sources: selectedTimelineSources,
|
|
2230
|
+
limit: 50,
|
|
2231
|
+
),
|
|
2232
|
+
const <String, dynamic>{'items': <dynamic>[]},
|
|
2233
|
+
);
|
|
2181
2234
|
final versionFuture = _softRefreshLoad<Map<String, dynamic>>(
|
|
2182
2235
|
'version',
|
|
2183
2236
|
_backendClient.fetchVersion(backendUrl),
|
|
@@ -2301,6 +2354,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
2301
2354
|
final providersResponse = await providersFuture;
|
|
2302
2355
|
final settingsResponse = await settingsFuture;
|
|
2303
2356
|
final runsResponse = await runsFuture;
|
|
2357
|
+
final timelineResponse = await timelineFuture;
|
|
2304
2358
|
final versionResponse = await versionFuture;
|
|
2305
2359
|
final tokenResponse = await tokenFuture;
|
|
2306
2360
|
final rateLimitResponse = await rateLimitFuture;
|
|
@@ -2348,6 +2402,12 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
2348
2402
|
RunSummary.fromJson,
|
|
2349
2403
|
fallbackToMapValues: true,
|
|
2350
2404
|
);
|
|
2405
|
+
timelineItems = _decodeModelList(
|
|
2406
|
+
'timeline',
|
|
2407
|
+
timelineResponse['items'],
|
|
2408
|
+
TimelineEventItem.fromJson,
|
|
2409
|
+
fallbackToMapValues: true,
|
|
2410
|
+
);
|
|
2351
2411
|
versionInfo = versionResponse;
|
|
2352
2412
|
backendHealthStatus = healthResponse;
|
|
2353
2413
|
tokenUsage = TokenUsageSnapshot.fromJson(tokenResponse);
|
|
@@ -2541,6 +2601,62 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
2541
2601
|
} catch (_) {}
|
|
2542
2602
|
}
|
|
2543
2603
|
|
|
2604
|
+
Future<void> refreshTimeline({
|
|
2605
|
+
Set<String>? sources,
|
|
2606
|
+
bool notify = true,
|
|
2607
|
+
}) async {
|
|
2608
|
+
if (!isAuthenticated) {
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2611
|
+
if (sources != null) {
|
|
2612
|
+
selectedTimelineSources = sources
|
|
2613
|
+
.map((value) => value.trim().toLowerCase())
|
|
2614
|
+
.where((value) => value.isNotEmpty)
|
|
2615
|
+
.toSet();
|
|
2616
|
+
}
|
|
2617
|
+
isRefreshingTimeline = true;
|
|
2618
|
+
if (notify) {
|
|
2619
|
+
notifyListeners();
|
|
2620
|
+
}
|
|
2621
|
+
try {
|
|
2622
|
+
final response = await _backendClient.fetchTimeline(
|
|
2623
|
+
backendUrl,
|
|
2624
|
+
sources: selectedTimelineSources,
|
|
2625
|
+
limit: 50,
|
|
2626
|
+
);
|
|
2627
|
+
timelineItems = _decodeModelList(
|
|
2628
|
+
'timeline',
|
|
2629
|
+
response['items'],
|
|
2630
|
+
TimelineEventItem.fromJson,
|
|
2631
|
+
fallbackToMapValues: true,
|
|
2632
|
+
);
|
|
2633
|
+
} catch (error) {
|
|
2634
|
+
errorMessage = _friendlyErrorMessage(error);
|
|
2635
|
+
} finally {
|
|
2636
|
+
isRefreshingTimeline = false;
|
|
2637
|
+
if (notify) {
|
|
2638
|
+
notifyListeners();
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
Future<void> toggleTimelineSource(String sourceKind) async {
|
|
2644
|
+
final normalized = sourceKind.trim().toLowerCase();
|
|
2645
|
+
if (normalized.isEmpty) {
|
|
2646
|
+
return;
|
|
2647
|
+
}
|
|
2648
|
+
final next = <String>{...selectedTimelineSources};
|
|
2649
|
+
if (next.contains(normalized)) {
|
|
2650
|
+
if (next.length == 1) {
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
2653
|
+
next.remove(normalized);
|
|
2654
|
+
} else {
|
|
2655
|
+
next.add(normalized);
|
|
2656
|
+
}
|
|
2657
|
+
await refreshTimeline(sources: next);
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2544
2660
|
Future<void> refreshMessaging() async {
|
|
2545
2661
|
try {
|
|
2546
2662
|
final statuses = await _backendClient.fetchMessagingStatus(
|
|
@@ -3875,6 +3991,19 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
3875
3991
|
}
|
|
3876
3992
|
}
|
|
3877
3993
|
|
|
3994
|
+
Future<void> setDesktopPassiveHistoryEnabled(bool value) async {
|
|
3995
|
+
final prefs = _prefs;
|
|
3996
|
+
if (prefs == null) {
|
|
3997
|
+
return;
|
|
3998
|
+
}
|
|
3999
|
+
try {
|
|
4000
|
+
await _desktopCompanion.setPassiveHistoryEnabled(value, prefs);
|
|
4001
|
+
} catch (error) {
|
|
4002
|
+
errorMessage = _friendlyErrorMessage(error);
|
|
4003
|
+
notifyListeners();
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
|
|
3878
4007
|
Future<void> rotateDesktopCompanionIdentity() async {
|
|
3879
4008
|
final prefs = _prefs;
|
|
3880
4009
|
if (prefs == null) {
|
|
@@ -4992,12 +5121,13 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
4992
5121
|
_backendClient.getBillingPlans(backendUrl),
|
|
4993
5122
|
_backendClient.getBillingInvoices(backendUrl),
|
|
4994
5123
|
]);
|
|
4995
|
-
billingSubscription =
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
billingInvoices = _asDynList(
|
|
5000
|
-
|
|
5124
|
+
billingSubscription = results[0]['subscription'] as Map<String, dynamic>?;
|
|
5125
|
+
billingPlans = _asDynList(
|
|
5126
|
+
results[1]['plans'],
|
|
5127
|
+
).cast<Map<String, dynamic>>();
|
|
5128
|
+
billingInvoices = _asDynList(
|
|
5129
|
+
results[2]['invoices'],
|
|
5130
|
+
).cast<Map<String, dynamic>>();
|
|
5001
5131
|
} catch (_) {
|
|
5002
5132
|
// retain previous data on error
|
|
5003
5133
|
} finally {
|
|
@@ -7001,6 +7131,9 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
7001
7131
|
}
|
|
7002
7132
|
unawaited(_refreshRecordingSessionById(sessionId));
|
|
7003
7133
|
});
|
|
7134
|
+
socket.on('timeline:updated', (dynamic _) {
|
|
7135
|
+
unawaited(refreshTimeline());
|
|
7136
|
+
});
|
|
7004
7137
|
socket.on('voice:session_ready', (dynamic data) {
|
|
7005
7138
|
final payload = _jsonMap(data);
|
|
7006
7139
|
voiceAssistantLiveState = voiceAssistantLiveState.copyWith(
|