neoagent 3.1.1-beta.8 → 3.2.0
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 +2 -2
- package/README.md +5 -1
- package/docs/architecture.md +1 -1
- package/docs/benchmarking.md +68 -87
- package/docs/capabilities.md +1 -1
- package/docs/clients-and-devices.md +3 -4
- package/docs/configuration.md +1 -1
- package/docs/devices.md +1 -2
- package/docs/health.md +24 -0
- package/docs/index.md +2 -2
- package/docs/integrations-architecture.md +6 -0
- package/docs/integrations.md +15 -1
- package/docs/models.md +1 -1
- package/docs/persistence.md +1 -1
- package/docs/why-neoagent.md +1 -1
- package/flutter_app/README.md +0 -5
- package/flutter_app/android/app/src/main/AndroidManifest.xml +0 -14
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +0 -98
- package/flutter_app/lib/main.dart +0 -6
- package/flutter_app/lib/main_app_shell.dart +0 -2
- package/flutter_app/lib/main_controller.dart +2 -867
- package/flutter_app/lib/main_devices.dart +0 -73
- package/flutter_app/lib/main_integrations.dart +339 -187
- package/flutter_app/lib/main_launcher.dart +2 -212
- package/flutter_app/lib/main_models.dart +0 -259
- package/flutter_app/lib/main_navigation.dart +2 -14
- package/flutter_app/lib/main_operations.dart +322 -364
- package/flutter_app/lib/main_runtime.dart +9 -208
- package/flutter_app/lib/main_settings.dart +17 -199
- package/flutter_app/lib/main_shared.dart +20 -591
- package/flutter_app/lib/main_timeline.dart +149 -438
- package/flutter_app/lib/main_voice_assistant.dart +0 -5
- package/flutter_app/lib/src/backend_client.dart +0 -102
- package/flutter_app/lib/src/desktop_companion_io.dart +1 -65
- package/flutter_app/lib/src/desktop_companion_stub.dart +0 -12
- package/flutter_app/linux/flutter/generated_plugin_registrant.cc +0 -4
- package/flutter_app/linux/flutter/generated_plugins.cmake +0 -1
- package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +0 -2
- package/flutter_app/macos/Podfile.lock +56 -5
- package/flutter_app/pubspec.lock +0 -7
- package/flutter_app/pubspec.yaml +0 -2
- package/flutter_app/web/index.html +174 -0
- package/flutter_app/windows/flutter/generated_plugin_registrant.cc +0 -3
- package/flutter_app/windows/flutter/generated_plugins.cmake +0 -1
- package/landing/index.html +0 -2
- package/lib/manager.js +1 -1
- package/lib/schema_migrations.js +75 -0
- package/package.json +1 -4
- package/server/db/database.js +1 -133
- package/server/http/middleware.js +2 -16
- package/server/http/routes.js +0 -2
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/NOTICES +0 -25
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/index.html +174 -0
- package/server/public/main.dart.js +88124 -90164
- package/server/routes/auth.js +11 -2
- package/server/routes/settings.js +0 -5
- package/server/routes/voice_assistant.js +1 -128
- package/server/services/account/erasure.js +0 -3
- package/server/services/ai/deliverables/artifact_helpers.js +1 -1
- package/server/services/ai/loop/conversation_loop.js +6 -2
- package/server/services/ai/loop/tool_dispatch.js +0 -3
- package/server/services/ai/messagingFallback.js +1 -1
- package/server/services/ai/settings.js +0 -6
- package/server/services/ai/toolEvidence.js +1 -4
- package/server/services/ai/tools.js +0 -234
- package/server/services/desktop/auth.js +0 -3
- package/server/services/desktop/registry.js +2 -50
- package/server/services/integrations/neoarchive/provider.js +616 -0
- package/server/services/integrations/neorecall/client.js +140 -0
- package/server/services/integrations/neorecall/constants.js +87 -0
- package/server/services/integrations/neorecall/provider.js +163 -0
- package/server/services/integrations/registry.js +4 -2
- package/server/services/manager.js +0 -22
- package/server/services/memory/manager.js +44 -19
- package/server/services/memory/retrieval_reasoning.js +36 -14
- package/server/services/messaging/whatsapp.js +18 -0
- package/server/services/security/tool_categories.js +0 -5
- package/server/services/social_video/service.js +2 -20
- package/server/services/tasks/adapters/index.js +1 -1
- package/server/services/tasks/integration_runtime.js +3 -67
- package/server/services/tasks/runtime.js +1 -6
- package/server/services/tasks/task_repository.js +6 -13
- package/server/services/timeline/service.js +0 -224
- package/server/services/voice/message.js +0 -24
- package/server/services/voice/providers.js +42 -1
- package/server/services/voice/runtimeManager.js +9 -5
- package/server/services/voice/turnRunner.js +0 -22
- package/server/services/wearable/service.js +0 -5
- package/docs/recordings-and-health.md +0 -41
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +0 -21
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +0 -586
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +0 -78
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +0 -104
- package/flutter_app/lib/main_recordings.dart +0 -920
- package/flutter_app/lib/src/desktop_ocr_bridge.dart +0 -2
- package/flutter_app/lib/src/desktop_ocr_bridge_io.dart +0 -125
- package/flutter_app/lib/src/desktop_ocr_bridge_stub.dart +0 -30
- package/flutter_app/lib/src/desktop_passive_history.dart +0 -332
- package/flutter_app/lib/src/recording_bridge.dart +0 -232
- package/flutter_app/lib/src/recording_bridge_io.dart +0 -1027
- package/flutter_app/lib/src/recording_bridge_stub.dart +0 -120
- package/flutter_app/lib/src/recording_bridge_web.dart +0 -702
- package/flutter_app/lib/src/recording_chunk_queue.dart +0 -149
- package/flutter_app/lib/src/recording_chunk_queue_io.dart +0 -182
- package/flutter_app/lib/src/recording_payloads.dart +0 -95
- package/flutter_app/third_party/desktop_audio_capture/LICENSE +0 -21
- package/flutter_app/third_party/desktop_audio_capture/README.md +0 -262
- package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +0 -65
- package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +0 -153
- package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +0 -110
- package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +0 -461
- package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +0 -91
- package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +0 -106
- package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +0 -219
- package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +0 -336
- package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +0 -101
- package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +0 -692
- package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +0 -35
- package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +0 -36
- package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +0 -32
- package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +0 -32
- package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +0 -878
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +0 -27
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +0 -1172
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +0 -655
- package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +0 -12
- package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +0 -30
- package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +0 -87
- package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +0 -105
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +0 -80
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +0 -31
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +0 -12
- package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +0 -23
- package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +0 -25
- package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +0 -1117
- package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +0 -115
- package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +0 -777
- package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +0 -87
- package/server/routes/recordings.js +0 -335
- package/server/routes/screenHistory.js +0 -190
- package/server/services/ai/recordingInsights.js +0 -105
- package/server/services/integrations/neomail/provider.js +0 -993
- package/server/services/recordings/deepgram.js +0 -58
- package/server/services/recordings/manager.js +0 -1273
- package/server/services/tasks/adapters/neomail_email_received.js +0 -46
- package/server/services/voice/screenshotContext.js +0 -73
- package/server/services/voice/turnCoordinator.js +0 -51
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
part of 'main.dart';
|
|
2
2
|
|
|
3
|
-
enum _LauncherPage { assistant, widgets,
|
|
3
|
+
enum _LauncherPage { assistant, widgets, settings }
|
|
4
4
|
|
|
5
5
|
class LauncherHomeView extends StatefulWidget {
|
|
6
6
|
const LauncherHomeView({super.key, required this.controller});
|
|
@@ -13,7 +13,6 @@ class LauncherHomeView extends StatefulWidget {
|
|
|
13
13
|
|
|
14
14
|
class _LauncherHomeViewState extends State<LauncherHomeView> {
|
|
15
15
|
static const int _assistantButtonKeyCode = 131;
|
|
16
|
-
static const int _recordingButtonKeyCode = 132;
|
|
17
16
|
|
|
18
17
|
final AndroidLauncherBridge _launcherBridge = AndroidLauncherBridge.instance;
|
|
19
18
|
StreamSubscription<LauncherHardwareButtonEvent>? _buttonSubscription;
|
|
@@ -22,7 +21,6 @@ class _LauncherHomeViewState extends State<LauncherHomeView> {
|
|
|
22
21
|
LauncherVolumeState? _volumeState;
|
|
23
22
|
LauncherDeviceStatus? _deviceStatus;
|
|
24
23
|
bool _assistantHardwareCaptureActive = false;
|
|
25
|
-
bool _recordingConfirmOpen = false;
|
|
26
24
|
DateTime _now = DateTime.now();
|
|
27
25
|
Timer? _statusTimer;
|
|
28
26
|
|
|
@@ -223,59 +221,6 @@ class _LauncherHomeViewState extends State<LauncherHomeView> {
|
|
|
223
221
|
});
|
|
224
222
|
}
|
|
225
223
|
|
|
226
|
-
Future<void> _toggleRecordingFromHardware() async {
|
|
227
|
-
final controller = widget.controller;
|
|
228
|
-
if (controller.recordingRuntime.active) {
|
|
229
|
-
await controller.stopRecording(stopReason: 'hardware_button');
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
if (!await _confirmRecordingStart(sourceLabel: 'hardware button')) {
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
await controller.startBackgroundRecording();
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
Future<void> _startRecordingFromUi() async {
|
|
239
|
-
final controller = widget.controller;
|
|
240
|
-
if (controller.recordingRuntime.active || controller.isStartingRecording) {
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
if (!await _confirmRecordingStart(sourceLabel: 'Start button')) {
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
await controller.startBackgroundRecording();
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
Future<bool> _confirmRecordingStart({required String sourceLabel}) async {
|
|
250
|
-
if (!mounted || _recordingConfirmOpen) {
|
|
251
|
-
return false;
|
|
252
|
-
}
|
|
253
|
-
_recordingConfirmOpen = true;
|
|
254
|
-
final confirmed = await showDialog<bool>(
|
|
255
|
-
context: context,
|
|
256
|
-
builder: (context) {
|
|
257
|
-
return AlertDialog(
|
|
258
|
-
title: const Text('Start recording?'),
|
|
259
|
-
content: Text(
|
|
260
|
-
'Recording was requested from $sourceLabel. Start a new background recording session now?',
|
|
261
|
-
),
|
|
262
|
-
actions: <Widget>[
|
|
263
|
-
TextButton(
|
|
264
|
-
onPressed: () => Navigator.of(context).pop(false),
|
|
265
|
-
child: const Text('Cancel'),
|
|
266
|
-
),
|
|
267
|
-
FilledButton(
|
|
268
|
-
onPressed: () => Navigator.of(context).pop(true),
|
|
269
|
-
child: const Text('Start'),
|
|
270
|
-
),
|
|
271
|
-
],
|
|
272
|
-
);
|
|
273
|
-
},
|
|
274
|
-
);
|
|
275
|
-
_recordingConfirmOpen = false;
|
|
276
|
-
return confirmed == true;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
224
|
Future<void> _startAssistantFromHardware() async {
|
|
280
225
|
if (_assistantHardwareCaptureActive) {
|
|
281
226
|
return;
|
|
@@ -308,11 +253,6 @@ class _LauncherHomeViewState extends State<LauncherHomeView> {
|
|
|
308
253
|
} else if (event.isUp) {
|
|
309
254
|
unawaited(_stopAssistantFromHardware());
|
|
310
255
|
}
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (event.keyCode == _recordingButtonKeyCode && event.isUp) {
|
|
315
|
-
unawaited(_toggleRecordingFromHardware());
|
|
316
256
|
}
|
|
317
257
|
}
|
|
318
258
|
|
|
@@ -320,147 +260,6 @@ class _LauncherHomeViewState extends State<LauncherHomeView> {
|
|
|
320
260
|
return VoiceAssistantPanel(controller: widget.controller);
|
|
321
261
|
}
|
|
322
262
|
|
|
323
|
-
Widget _buildRecordingsPage() {
|
|
324
|
-
final controller = widget.controller;
|
|
325
|
-
final runtime = controller.recordingRuntime;
|
|
326
|
-
final recentSessions = controller.recordingSessions.take(4).toList();
|
|
327
|
-
return ListView(
|
|
328
|
-
padding: _pagePadding(context),
|
|
329
|
-
children: <Widget>[
|
|
330
|
-
_PageTitle(
|
|
331
|
-
title: 'Recorder',
|
|
332
|
-
subtitle:
|
|
333
|
-
'Record audio and keep it running while you use the device.',
|
|
334
|
-
trailing: Wrap(
|
|
335
|
-
spacing: 10,
|
|
336
|
-
runSpacing: 10,
|
|
337
|
-
children: <Widget>[
|
|
338
|
-
FilledButton.icon(
|
|
339
|
-
onPressed: controller.isStartingRecording || runtime.active
|
|
340
|
-
? null
|
|
341
|
-
: _startRecordingFromUi,
|
|
342
|
-
icon: const Icon(Icons.mic_none_outlined),
|
|
343
|
-
label: const Text('Start'),
|
|
344
|
-
),
|
|
345
|
-
OutlinedButton.icon(
|
|
346
|
-
onPressed: runtime.active
|
|
347
|
-
? (runtime.paused
|
|
348
|
-
? controller.resumeBackgroundRecording
|
|
349
|
-
: controller.pauseBackgroundRecording)
|
|
350
|
-
: null,
|
|
351
|
-
icon: Icon(
|
|
352
|
-
runtime.paused
|
|
353
|
-
? Icons.play_circle_outline
|
|
354
|
-
: Icons.pause_circle_outline,
|
|
355
|
-
),
|
|
356
|
-
label: Text(runtime.paused ? 'Resume' : 'Pause'),
|
|
357
|
-
),
|
|
358
|
-
OutlinedButton.icon(
|
|
359
|
-
onPressed: runtime.active && !controller.isStoppingRecording
|
|
360
|
-
? controller.stopRecording
|
|
361
|
-
: null,
|
|
362
|
-
icon: const Icon(Icons.stop_circle_outlined),
|
|
363
|
-
label: const Text('Stop'),
|
|
364
|
-
),
|
|
365
|
-
],
|
|
366
|
-
),
|
|
367
|
-
),
|
|
368
|
-
Card(
|
|
369
|
-
child: Padding(
|
|
370
|
-
padding: const EdgeInsets.all(18),
|
|
371
|
-
child: Column(
|
|
372
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
373
|
-
children: <Widget>[
|
|
374
|
-
Wrap(
|
|
375
|
-
spacing: 10,
|
|
376
|
-
runSpacing: 10,
|
|
377
|
-
children: <Widget>[
|
|
378
|
-
_DotStatus(
|
|
379
|
-
label: runtime.active
|
|
380
|
-
? (runtime.paused ? 'Paused' : 'Recording')
|
|
381
|
-
: 'Idle',
|
|
382
|
-
color: runtime.active
|
|
383
|
-
? (runtime.paused ? _warning : _danger)
|
|
384
|
-
: _success,
|
|
385
|
-
),
|
|
386
|
-
_MetaPill(
|
|
387
|
-
icon: Icons.mic_outlined,
|
|
388
|
-
label: runtime.supportsBackgroundMic
|
|
389
|
-
? 'Background recording ready'
|
|
390
|
-
: 'Recording unavailable',
|
|
391
|
-
),
|
|
392
|
-
],
|
|
393
|
-
),
|
|
394
|
-
if (runtime.errorMessage != null &&
|
|
395
|
-
runtime.errorMessage!.trim().isNotEmpty) ...<Widget>[
|
|
396
|
-
const SizedBox(height: 14),
|
|
397
|
-
_InlineError(message: runtime.errorMessage!),
|
|
398
|
-
],
|
|
399
|
-
],
|
|
400
|
-
),
|
|
401
|
-
),
|
|
402
|
-
),
|
|
403
|
-
const SizedBox(height: 16),
|
|
404
|
-
Card(
|
|
405
|
-
child: Padding(
|
|
406
|
-
padding: const EdgeInsets.all(18),
|
|
407
|
-
child: Column(
|
|
408
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
409
|
-
children: <Widget>[
|
|
410
|
-
const _SectionTitle('Recent Sessions'),
|
|
411
|
-
const SizedBox(height: 10),
|
|
412
|
-
if (recentSessions.isEmpty)
|
|
413
|
-
Text(
|
|
414
|
-
'No recordings yet.',
|
|
415
|
-
style: TextStyle(color: _textSecondary),
|
|
416
|
-
)
|
|
417
|
-
else
|
|
418
|
-
...recentSessions.map(
|
|
419
|
-
(session) => Padding(
|
|
420
|
-
padding: const EdgeInsets.only(bottom: 12),
|
|
421
|
-
child: Row(
|
|
422
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
423
|
-
children: <Widget>[
|
|
424
|
-
Icon(Icons.audio_file_outlined, color: _accent),
|
|
425
|
-
const SizedBox(width: 10),
|
|
426
|
-
Expanded(
|
|
427
|
-
child: Column(
|
|
428
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
429
|
-
children: <Widget>[
|
|
430
|
-
Text(
|
|
431
|
-
session.title,
|
|
432
|
-
style: const TextStyle(
|
|
433
|
-
fontWeight: FontWeight.w700,
|
|
434
|
-
),
|
|
435
|
-
),
|
|
436
|
-
const SizedBox(height: 4),
|
|
437
|
-
Text(
|
|
438
|
-
'${session.statusLabel} • ${session.startedAtLabel}',
|
|
439
|
-
style: TextStyle(color: _textSecondary),
|
|
440
|
-
),
|
|
441
|
-
],
|
|
442
|
-
),
|
|
443
|
-
),
|
|
444
|
-
],
|
|
445
|
-
),
|
|
446
|
-
),
|
|
447
|
-
),
|
|
448
|
-
Align(
|
|
449
|
-
alignment: Alignment.centerLeft,
|
|
450
|
-
child: OutlinedButton.icon(
|
|
451
|
-
onPressed: controller.refreshRecordings,
|
|
452
|
-
icon: const Icon(Icons.refresh),
|
|
453
|
-
label: const Text('Refresh'),
|
|
454
|
-
),
|
|
455
|
-
),
|
|
456
|
-
],
|
|
457
|
-
),
|
|
458
|
-
),
|
|
459
|
-
),
|
|
460
|
-
],
|
|
461
|
-
);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
263
|
Widget _buildWidgetsPage() {
|
|
465
264
|
final controller = widget.controller;
|
|
466
265
|
return ListView(
|
|
@@ -637,7 +436,7 @@ class _LauncherHomeViewState extends State<LauncherHomeView> {
|
|
|
637
436
|
const _SectionTitle('Extra Buttons'),
|
|
638
437
|
const SizedBox(height: 10),
|
|
639
438
|
Text(
|
|
640
|
-
'Hold button 131 for the assistant.
|
|
439
|
+
'Hold button 131 for the assistant.',
|
|
641
440
|
style: TextStyle(color: _textSecondary, height: 1.45),
|
|
642
441
|
),
|
|
643
442
|
const SizedBox(height: 16),
|
|
@@ -649,10 +448,6 @@ class _LauncherHomeViewState extends State<LauncherHomeView> {
|
|
|
649
448
|
icon: Icons.keyboard_voice_outlined,
|
|
650
449
|
label: 'Assistant key 131',
|
|
651
450
|
),
|
|
652
|
-
const _MetaPill(
|
|
653
|
-
icon: Icons.fiber_smart_record_outlined,
|
|
654
|
-
label: 'Recording key 132',
|
|
655
|
-
),
|
|
656
451
|
],
|
|
657
452
|
),
|
|
658
453
|
],
|
|
@@ -911,7 +706,6 @@ class _LauncherHomeViewState extends State<LauncherHomeView> {
|
|
|
911
706
|
final pages = <Widget>[
|
|
912
707
|
_buildAssistantPage(),
|
|
913
708
|
_buildWidgetsPage(),
|
|
914
|
-
_buildRecordingsPage(),
|
|
915
709
|
_buildSettingsPage(),
|
|
916
710
|
];
|
|
917
711
|
return IndexedStack(
|
|
@@ -938,10 +732,6 @@ class _LauncherHomeViewState extends State<LauncherHomeView> {
|
|
|
938
732
|
icon: Icon(Icons.dashboard_customize_outlined),
|
|
939
733
|
label: 'Widgets',
|
|
940
734
|
),
|
|
941
|
-
NavigationDestination(
|
|
942
|
-
icon: Icon(Icons.fiber_smart_record_outlined),
|
|
943
|
-
label: 'Record',
|
|
944
|
-
),
|
|
945
735
|
NavigationDestination(
|
|
946
736
|
icon: Icon(Icons.tune_outlined),
|
|
947
737
|
label: 'Settings',
|
|
@@ -1143,239 +1143,6 @@ MessagingAccessRule? _ruleFromPrefixedEntry(String platform, String entry) {
|
|
|
1143
1143
|
return MessagingAccessRule(scope: 'chat', value: normalized);
|
|
1144
1144
|
}
|
|
1145
1145
|
|
|
1146
|
-
class RecordingSessionItem {
|
|
1147
|
-
const RecordingSessionItem({
|
|
1148
|
-
required this.id,
|
|
1149
|
-
required this.title,
|
|
1150
|
-
required this.platform,
|
|
1151
|
-
required this.status,
|
|
1152
|
-
required this.startedAt,
|
|
1153
|
-
required this.endedAt,
|
|
1154
|
-
required this.durationMs,
|
|
1155
|
-
required this.transcriptText,
|
|
1156
|
-
required this.lastError,
|
|
1157
|
-
required this.sources,
|
|
1158
|
-
required this.transcriptSegments,
|
|
1159
|
-
this.structuredContent = const <String, dynamic>{},
|
|
1160
|
-
});
|
|
1161
|
-
|
|
1162
|
-
factory RecordingSessionItem.fromJson(Map<dynamic, dynamic> json) {
|
|
1163
|
-
final rawSources = json['sources'];
|
|
1164
|
-
final sourceRows = rawSources is List
|
|
1165
|
-
? rawSources
|
|
1166
|
-
: rawSources is Map
|
|
1167
|
-
? rawSources.values.toList(growable: false)
|
|
1168
|
-
: const <dynamic>[];
|
|
1169
|
-
final rawSegments = json['transcriptSegments'];
|
|
1170
|
-
final transcriptSegmentRows = rawSegments is List
|
|
1171
|
-
? rawSegments
|
|
1172
|
-
: rawSegments is Map
|
|
1173
|
-
? rawSegments.values.toList(growable: false)
|
|
1174
|
-
: const <dynamic>[];
|
|
1175
|
-
return RecordingSessionItem(
|
|
1176
|
-
id: json['id']?.toString() ?? '',
|
|
1177
|
-
title: json['title']?.toString().ifEmpty('Recording') ?? 'Recording',
|
|
1178
|
-
platform: json['platform']?.toString() ?? 'unknown',
|
|
1179
|
-
status: json['status']?.toString() ?? 'recording',
|
|
1180
|
-
startedAt: _parseTimestamp(json['startedAt']?.toString()),
|
|
1181
|
-
endedAt: _parseOptionalTimestamp(json['endedAt']?.toString()),
|
|
1182
|
-
durationMs: _asInt(json['durationMs']),
|
|
1183
|
-
transcriptText: json['transcriptText']?.toString() ?? '',
|
|
1184
|
-
lastError: json['lastError']?.toString(),
|
|
1185
|
-
sources: sourceRows
|
|
1186
|
-
.whereType<Map<dynamic, dynamic>>()
|
|
1187
|
-
.map(RecordingSourceItem.fromJson)
|
|
1188
|
-
.toList(),
|
|
1189
|
-
transcriptSegments: transcriptSegmentRows
|
|
1190
|
-
.whereType<Map<dynamic, dynamic>>()
|
|
1191
|
-
.map(RecordingTranscriptSegment.fromJson)
|
|
1192
|
-
.toList(),
|
|
1193
|
-
structuredContent: _jsonMap(json['structuredContent']),
|
|
1194
|
-
);
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
final String id;
|
|
1198
|
-
final String title;
|
|
1199
|
-
final String platform;
|
|
1200
|
-
final String status;
|
|
1201
|
-
final DateTime startedAt;
|
|
1202
|
-
final DateTime? endedAt;
|
|
1203
|
-
final int durationMs;
|
|
1204
|
-
final String transcriptText;
|
|
1205
|
-
final String? lastError;
|
|
1206
|
-
final List<RecordingSourceItem> sources;
|
|
1207
|
-
final List<RecordingTranscriptSegment> transcriptSegments;
|
|
1208
|
-
final Map<String, dynamic> structuredContent;
|
|
1209
|
-
|
|
1210
|
-
String get startedAtLabel => _formatTimestamp(startedAt);
|
|
1211
|
-
|
|
1212
|
-
String get platformLabel {
|
|
1213
|
-
switch (platform) {
|
|
1214
|
-
case 'web':
|
|
1215
|
-
return 'Web';
|
|
1216
|
-
case 'android':
|
|
1217
|
-
return 'Android';
|
|
1218
|
-
default:
|
|
1219
|
-
return platform.ifEmpty('Unknown');
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
String get durationLabel => _formatDuration(durationMs);
|
|
1224
|
-
|
|
1225
|
-
String get statusLabel {
|
|
1226
|
-
switch (status) {
|
|
1227
|
-
case 'recording':
|
|
1228
|
-
return 'Recording';
|
|
1229
|
-
case 'processing':
|
|
1230
|
-
return 'Processing';
|
|
1231
|
-
case 'completed':
|
|
1232
|
-
return 'Completed';
|
|
1233
|
-
case 'failed':
|
|
1234
|
-
return 'Failed';
|
|
1235
|
-
case 'cancelled':
|
|
1236
|
-
return 'Cancelled';
|
|
1237
|
-
default:
|
|
1238
|
-
return status;
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
Color get statusColor {
|
|
1243
|
-
switch (status) {
|
|
1244
|
-
case 'recording':
|
|
1245
|
-
return _danger;
|
|
1246
|
-
case 'processing':
|
|
1247
|
-
return _warning;
|
|
1248
|
-
case 'completed':
|
|
1249
|
-
return _success;
|
|
1250
|
-
case 'failed':
|
|
1251
|
-
return _danger;
|
|
1252
|
-
case 'cancelled':
|
|
1253
|
-
return _textSecondary;
|
|
1254
|
-
default:
|
|
1255
|
-
return _textSecondary;
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
class RecordingSourceItem {
|
|
1261
|
-
const RecordingSourceItem({
|
|
1262
|
-
required this.sourceKey,
|
|
1263
|
-
required this.sourceKind,
|
|
1264
|
-
required this.mediaKind,
|
|
1265
|
-
required this.mimeType,
|
|
1266
|
-
required this.durationMs,
|
|
1267
|
-
required this.chunkCount,
|
|
1268
|
-
});
|
|
1269
|
-
|
|
1270
|
-
factory RecordingSourceItem.fromJson(Map<dynamic, dynamic> json) {
|
|
1271
|
-
return RecordingSourceItem(
|
|
1272
|
-
sourceKey: json['sourceKey']?.toString() ?? '',
|
|
1273
|
-
sourceKind: json['sourceKind']?.toString() ?? '',
|
|
1274
|
-
mediaKind: json['mediaKind']?.toString() ?? '',
|
|
1275
|
-
mimeType: json['mimeType']?.toString() ?? '',
|
|
1276
|
-
durationMs: _asInt(json['durationMs']),
|
|
1277
|
-
chunkCount: _asInt(json['chunkCount']),
|
|
1278
|
-
);
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
final String sourceKey;
|
|
1282
|
-
final String sourceKind;
|
|
1283
|
-
final String mediaKind;
|
|
1284
|
-
final String mimeType;
|
|
1285
|
-
final int durationMs;
|
|
1286
|
-
final int chunkCount;
|
|
1287
|
-
|
|
1288
|
-
String get label {
|
|
1289
|
-
switch (sourceKind) {
|
|
1290
|
-
case 'screen-share':
|
|
1291
|
-
return 'Screen';
|
|
1292
|
-
case 'microphone':
|
|
1293
|
-
return 'Microphone';
|
|
1294
|
-
default:
|
|
1295
|
-
return sourceKind.ifEmpty(sourceKey);
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
String get durationLabel => _formatDuration(durationMs);
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
class RecordingTranscriptSegment {
|
|
1303
|
-
const RecordingTranscriptSegment({
|
|
1304
|
-
required this.id,
|
|
1305
|
-
required this.speaker,
|
|
1306
|
-
required this.text,
|
|
1307
|
-
required this.startMs,
|
|
1308
|
-
});
|
|
1309
|
-
|
|
1310
|
-
factory RecordingTranscriptSegment.fromJson(Map<dynamic, dynamic> json) {
|
|
1311
|
-
return RecordingTranscriptSegment(
|
|
1312
|
-
id: _asInt(json['id']),
|
|
1313
|
-
speaker:
|
|
1314
|
-
json['speaker']?.toString() ?? json['sourceKey']?.toString() ?? '',
|
|
1315
|
-
text: json['text']?.toString() ?? '',
|
|
1316
|
-
startMs: _asInt(json['startMs']),
|
|
1317
|
-
);
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
final int id;
|
|
1321
|
-
final String speaker;
|
|
1322
|
-
final String text;
|
|
1323
|
-
final int startMs;
|
|
1324
|
-
|
|
1325
|
-
String get timestampLabel => _formatDuration(startMs);
|
|
1326
|
-
|
|
1327
|
-
String get displayText {
|
|
1328
|
-
if (speaker.trim().isEmpty) {
|
|
1329
|
-
return text;
|
|
1330
|
-
}
|
|
1331
|
-
return '${speaker.replaceAll('-', ' ')}: $text';
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
class VoiceAssistantTurnResult {
|
|
1336
|
-
const VoiceAssistantTurnResult({
|
|
1337
|
-
required this.session,
|
|
1338
|
-
required this.transcript,
|
|
1339
|
-
required this.replyText,
|
|
1340
|
-
required this.audioMimeType,
|
|
1341
|
-
required this.audioBytes,
|
|
1342
|
-
this.runId,
|
|
1343
|
-
this.ttsProvider,
|
|
1344
|
-
this.ttsModel,
|
|
1345
|
-
this.ttsVoice,
|
|
1346
|
-
this.ttsError,
|
|
1347
|
-
});
|
|
1348
|
-
|
|
1349
|
-
factory VoiceAssistantTurnResult.fromJson(Map<dynamic, dynamic> json) {
|
|
1350
|
-
final audioBase64 = json['audioBase64']?.toString() ?? '';
|
|
1351
|
-
return VoiceAssistantTurnResult(
|
|
1352
|
-
session: RecordingSessionItem.fromJson(_jsonMap(json['session'])),
|
|
1353
|
-
transcript: json['transcript']?.toString() ?? '',
|
|
1354
|
-
replyText: json['replyText']?.toString() ?? '',
|
|
1355
|
-
audioMimeType: json['audioMimeType']?.toString() ?? 'audio/mpeg',
|
|
1356
|
-
audioBytes: audioBase64.trim().isEmpty
|
|
1357
|
-
? Uint8List(0)
|
|
1358
|
-
: base64Decode(audioBase64),
|
|
1359
|
-
runId: json['runId']?.toString(),
|
|
1360
|
-
ttsProvider: json['ttsProvider']?.toString(),
|
|
1361
|
-
ttsModel: json['ttsModel']?.toString(),
|
|
1362
|
-
ttsVoice: json['ttsVoice']?.toString(),
|
|
1363
|
-
ttsError: json['ttsError']?.toString(),
|
|
1364
|
-
);
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
final RecordingSessionItem session;
|
|
1368
|
-
final String transcript;
|
|
1369
|
-
final String replyText;
|
|
1370
|
-
final String audioMimeType;
|
|
1371
|
-
final Uint8List audioBytes;
|
|
1372
|
-
final String? runId;
|
|
1373
|
-
final String? ttsProvider;
|
|
1374
|
-
final String? ttsModel;
|
|
1375
|
-
final String? ttsVoice;
|
|
1376
|
-
final String? ttsError;
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
1146
|
class MemoryTransferImportResult {
|
|
1380
1147
|
const MemoryTransferImportResult({
|
|
1381
1148
|
required this.importedCount,
|
|
@@ -2399,52 +2166,26 @@ class TimelineEventItem {
|
|
|
2399
2166
|
String get occurredAtLabel => _formatTimestamp(occurredAt);
|
|
2400
2167
|
|
|
2401
2168
|
String get sourceLabel => switch (sourceKind) {
|
|
2402
|
-
'screen' => 'Screen',
|
|
2403
2169
|
'tasks' => 'Task',
|
|
2404
2170
|
'runs' => 'Run',
|
|
2405
2171
|
_ => _titleCase(sourceKind.replaceAll('_', ' ')),
|
|
2406
2172
|
};
|
|
2407
2173
|
|
|
2408
2174
|
Color get sourceColor => switch (sourceKind) {
|
|
2409
|
-
'screen' => _accent,
|
|
2410
2175
|
'tasks' => _warning,
|
|
2411
2176
|
'runs' => _success,
|
|
2412
2177
|
_ => _textSecondary,
|
|
2413
2178
|
};
|
|
2414
2179
|
|
|
2415
|
-
String get appName => metadata['appName']?.toString() ?? '';
|
|
2416
|
-
String get windowTitle => metadata['windowTitle']?.toString() ?? '';
|
|
2417
2180
|
String get deviceLabel =>
|
|
2418
2181
|
metadata['deviceLabel']?.toString() ??
|
|
2419
2182
|
metadata['deviceId']?.toString() ??
|
|
2420
2183
|
'';
|
|
2421
|
-
String get previewText => metadata['previewText']?.toString() ?? summary;
|
|
2422
2184
|
String get runId => metadata['runId']?.toString() ?? sourceId ?? '';
|
|
2423
2185
|
String get taskName =>
|
|
2424
2186
|
metadata['taskName']?.toString().trim().isNotEmpty == true
|
|
2425
2187
|
? metadata['taskName'].toString()
|
|
2426
2188
|
: title;
|
|
2427
|
-
DateTime? get startedAt =>
|
|
2428
|
-
_parseOptionalTimestamp(metadata['startedAt']?.toString());
|
|
2429
|
-
DateTime? get endedAt =>
|
|
2430
|
-
_parseOptionalTimestamp(metadata['endedAt']?.toString());
|
|
2431
|
-
|
|
2432
|
-
String get screenSpanLabel {
|
|
2433
|
-
final start = startedAt;
|
|
2434
|
-
final end = endedAt;
|
|
2435
|
-
if (start == null) {
|
|
2436
|
-
return occurredAtLabel;
|
|
2437
|
-
}
|
|
2438
|
-
final localStart = start.toLocal();
|
|
2439
|
-
final startMinute = localStart.minute.toString().padLeft(2, '0');
|
|
2440
|
-
if (end == null) {
|
|
2441
|
-
return '${localStart.hour.toString().padLeft(2, '0')}:$startMinute';
|
|
2442
|
-
}
|
|
2443
|
-
final localEnd = end.toLocal();
|
|
2444
|
-
final endMinute = localEnd.minute.toString().padLeft(2, '0');
|
|
2445
|
-
return '${localStart.hour.toString().padLeft(2, '0')}:$startMinute'
|
|
2446
|
-
' - ${localEnd.hour.toString().padLeft(2, '0')}:$endMinute';
|
|
2447
|
-
}
|
|
2448
2189
|
}
|
|
2449
2190
|
|
|
2450
2191
|
class TokenUsageSnapshot {
|
|
@@ -17,7 +17,6 @@ enum AppSection {
|
|
|
17
17
|
timeline,
|
|
18
18
|
voiceAssistant,
|
|
19
19
|
devices,
|
|
20
|
-
recordings,
|
|
21
20
|
messaging,
|
|
22
21
|
runs,
|
|
23
22
|
settings,
|
|
@@ -34,7 +33,7 @@ enum AppSection {
|
|
|
34
33
|
billing,
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
enum SidebarGroup { chat, timeline,
|
|
36
|
+
enum SidebarGroup { chat, timeline, automation, settings }
|
|
38
37
|
|
|
39
38
|
extension SidebarGroupX on SidebarGroup {
|
|
40
39
|
String get label {
|
|
@@ -43,8 +42,6 @@ extension SidebarGroupX on SidebarGroup {
|
|
|
43
42
|
return 'Chat';
|
|
44
43
|
case SidebarGroup.timeline:
|
|
45
44
|
return 'Timeline';
|
|
46
|
-
case SidebarGroup.recordings:
|
|
47
|
-
return 'Recordings';
|
|
48
45
|
case SidebarGroup.automation:
|
|
49
46
|
return 'Automation';
|
|
50
47
|
case SidebarGroup.settings:
|
|
@@ -58,8 +55,6 @@ extension SidebarGroupX on SidebarGroup {
|
|
|
58
55
|
return Icons.chat_bubble_outline;
|
|
59
56
|
case SidebarGroup.timeline:
|
|
60
57
|
return Icons.timeline_rounded;
|
|
61
|
-
case SidebarGroup.recordings:
|
|
62
|
-
return Icons.fiber_smart_record_outlined;
|
|
63
58
|
case SidebarGroup.automation:
|
|
64
59
|
return Icons.auto_awesome_outlined;
|
|
65
60
|
case SidebarGroup.settings:
|
|
@@ -79,8 +74,6 @@ extension AppSectionX on AppSection {
|
|
|
79
74
|
return 'Voice assistant';
|
|
80
75
|
case AppSection.devices:
|
|
81
76
|
return 'Devices';
|
|
82
|
-
case AppSection.recordings:
|
|
83
|
-
return 'Recordings';
|
|
84
77
|
case AppSection.messaging:
|
|
85
78
|
return 'Messaging';
|
|
86
79
|
case AppSection.runs:
|
|
@@ -122,8 +115,6 @@ extension AppSectionX on AppSection {
|
|
|
122
115
|
return Icons.keyboard_voice_outlined;
|
|
123
116
|
case AppSection.devices:
|
|
124
117
|
return Icons.devices_other_outlined;
|
|
125
|
-
case AppSection.recordings:
|
|
126
|
-
return Icons.fiber_smart_record_outlined;
|
|
127
118
|
case AppSection.messaging:
|
|
128
119
|
return Icons.forum_outlined;
|
|
129
120
|
case AppSection.runs:
|
|
@@ -162,8 +153,6 @@ extension AppSectionX on AppSection {
|
|
|
162
153
|
return SidebarGroup.chat;
|
|
163
154
|
case AppSection.timeline:
|
|
164
155
|
return SidebarGroup.timeline;
|
|
165
|
-
case AppSection.recordings:
|
|
166
|
-
return SidebarGroup.recordings;
|
|
167
156
|
case AppSection.devices:
|
|
168
157
|
case AppSection.skills:
|
|
169
158
|
case AppSection.integrations:
|
|
@@ -210,8 +199,7 @@ extension AppSectionX on AppSection {
|
|
|
210
199
|
return effectiveSection.label;
|
|
211
200
|
}
|
|
212
201
|
if (effectiveSection.group == SidebarGroup.chat ||
|
|
213
|
-
effectiveSection.group == SidebarGroup.timeline
|
|
214
|
-
effectiveSection.group == SidebarGroup.recordings) {
|
|
202
|
+
effectiveSection.group == SidebarGroup.timeline) {
|
|
215
203
|
return groupLabel;
|
|
216
204
|
}
|
|
217
205
|
if (groupLabel == effectiveSection.label) {
|