neoagent 3.0.1-beta.2 → 3.0.1-beta.21

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.
Files changed (94) hide show
  1. package/.env.example +19 -0
  2. package/README.md +20 -3
  3. package/docs/benchmarking.md +102 -0
  4. package/docs/billing.md +224 -0
  5. package/docs/configuration.md +22 -0
  6. package/docs/getting-started.md +10 -8
  7. package/docs/index.md +1 -0
  8. package/docs/operations.md +1 -1
  9. package/flutter_app/lib/main.dart +4 -1
  10. package/flutter_app/lib/main_account_settings.dart +138 -0
  11. package/flutter_app/lib/main_app_shell.dart +316 -0
  12. package/flutter_app/lib/main_billing.dart +1465 -0
  13. package/flutter_app/lib/main_chat.dart +1594 -224
  14. package/flutter_app/lib/main_controller.dart +263 -26
  15. package/flutter_app/lib/main_devices.dart +1 -1
  16. package/flutter_app/lib/main_install.dart +1147 -0
  17. package/flutter_app/lib/main_navigation.dart +12 -0
  18. package/flutter_app/lib/main_operations.dart +134 -9
  19. package/flutter_app/lib/main_settings.dart +148 -52
  20. package/flutter_app/lib/main_shared.dart +1 -0
  21. package/flutter_app/lib/src/backend_client.dart +86 -1
  22. package/landing/index.html +2 -2
  23. package/lib/manager.js +184 -66
  24. package/lib/schema_migrations.js +84 -0
  25. package/package.json +10 -4
  26. package/server/admin/access.js +12 -7
  27. package/server/admin/admin.css +78 -0
  28. package/server/admin/admin.js +511 -23
  29. package/server/admin/billing.js +415 -0
  30. package/server/admin/index.html +120 -13
  31. package/server/admin/users.js +15 -15
  32. package/server/db/database.js +13 -21
  33. package/server/db/sessions_db.js +8 -0
  34. package/server/http/middleware.js +4 -4
  35. package/server/http/routes.js +9 -0
  36. package/server/http/static.js +4 -2
  37. package/server/middleware/requireBilling.js +12 -0
  38. package/server/public/.last_build_id +1 -1
  39. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  40. package/server/public/flutter_bootstrap.js +1 -1
  41. package/server/public/main.dart.js +89347 -85449
  42. package/server/routes/account.js +53 -0
  43. package/server/routes/admin.js +515 -63
  44. package/server/routes/agents.js +203 -21
  45. package/server/routes/billing.js +127 -0
  46. package/server/routes/billing_webhook.js +43 -0
  47. package/server/routes/memory.js +1 -4
  48. package/server/routes/settings.js +1 -2
  49. package/server/routes/skills.js +1 -1
  50. package/server/routes/triggers.js +2 -2
  51. package/server/services/account/erasure.js +263 -0
  52. package/server/services/account/sessions.js +1 -9
  53. package/server/services/ai/loop/agent_engine_core.js +42 -0
  54. package/server/services/ai/loop/blank_recovery.js +36 -0
  55. package/server/services/ai/loop/completion_judge.js +42 -0
  56. package/server/services/ai/loop/conversation_loop.js +248 -34
  57. package/server/services/ai/loop/progress_monitor.js +6 -6
  58. package/server/services/ai/loopPolicy.js +37 -44
  59. package/server/services/ai/messagingFallback.js +25 -1
  60. package/server/services/ai/models.js +18 -0
  61. package/server/services/ai/preModelCompaction.js +25 -5
  62. package/server/services/ai/rate_limits.js +28 -4
  63. package/server/services/ai/systemPrompt.js +23 -5
  64. package/server/services/ai/taskAnalysis.js +2 -0
  65. package/server/services/ai/tools.js +231 -20
  66. package/server/services/android/controller.js +6 -2
  67. package/server/services/billing/billing_email.js +106 -0
  68. package/server/services/billing/config.js +17 -0
  69. package/server/services/billing/plans.js +121 -0
  70. package/server/services/billing/stripe_client.js +21 -0
  71. package/server/services/billing/subscriptions.js +462 -0
  72. package/server/services/billing/trial_guard.js +111 -0
  73. package/server/services/browser/contentExtractor.js +629 -0
  74. package/server/services/browser/controller.js +4 -8
  75. package/server/services/desktop/gateway.js +7 -4
  76. package/server/services/integrations/google/calendar.js +30 -2
  77. package/server/services/integrations/secrets.js +7 -4
  78. package/server/services/manager.js +11 -0
  79. package/server/services/messaging/automation.js +1 -1
  80. package/server/services/messaging/telnyx.js +12 -11
  81. package/server/services/messaging/whatsapp.js +1 -1
  82. package/server/services/recordings/manager.js +13 -7
  83. package/server/services/runtime/backends/local-vm.js +40 -22
  84. package/server/services/runtime/docker-vm-manager.js +157 -266
  85. package/server/services/runtime/guest_bootstrap.js +17 -5
  86. package/server/services/runtime/guest_image.js +182 -0
  87. package/server/services/runtime/manager.js +0 -1
  88. package/server/services/runtime/validation.js +3 -8
  89. package/server/services/tasks/runtime.js +103 -15
  90. package/server/services/tasks/schedule_utils.js +5 -5
  91. package/server/services/voice/runtimeManager.js +19 -10
  92. package/server/services/wearable/gateway.js +8 -5
  93. package/server/services/websocket.js +26 -8
  94. package/server/services/workspace/manager.js +37 -3
@@ -57,6 +57,7 @@ class BackendSetupView extends StatefulWidget {
57
57
 
58
58
  class _BackendSetupViewState extends State<BackendSetupView> {
59
59
  late final TextEditingController _backendUrlController;
60
+ bool _localInstall = false;
60
61
 
61
62
  @override
62
63
  void initState() {
@@ -78,6 +79,12 @@ class _BackendSetupViewState extends State<BackendSetupView> {
78
79
 
79
80
  @override
80
81
  Widget build(BuildContext context) {
82
+ if (_localInstall) {
83
+ return _LocalInstallWidget(
84
+ controller: widget.controller,
85
+ onBack: () => setState(() => _localInstall = false),
86
+ );
87
+ }
81
88
  final controller = widget.controller;
82
89
  return _AmbientBackdrop(
83
90
  child: Scaffold(
@@ -195,6 +202,200 @@ class _BackendSetupViewState extends State<BackendSetupView> {
195
202
  ),
196
203
  ),
197
204
  ),
205
+ const SizedBox(height: 10),
206
+ Center(
207
+ child: TextButton.icon(
208
+ onPressed: () => setState(() => _localInstall = true),
209
+ icon: const Icon(Icons.download_outlined, size: 16),
210
+ label: const Text('Install NeoAgent on this machine'),
211
+ ),
212
+ ),
213
+ ],
214
+ ),
215
+ ),
216
+ ),
217
+ ),
218
+ ),
219
+ ),
220
+ ),
221
+ ),
222
+ ),
223
+ );
224
+ }
225
+ }
226
+
227
+ // ─── Local Install Widget ────────────────────────────────────────────────────
228
+
229
+ enum _LocalInstallPhase { checking, ready, installing, done, failed }
230
+
231
+ class _LocalInstallWidget extends StatefulWidget {
232
+ const _LocalInstallWidget({required this.controller, required this.onBack});
233
+
234
+ final NeoAgentController controller;
235
+ final VoidCallback onBack;
236
+
237
+ @override
238
+ State<_LocalInstallWidget> createState() => _LocalInstallWidgetState();
239
+ }
240
+
241
+ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
242
+ _LocalInstallPhase _phase = _LocalInstallPhase.checking;
243
+ String? _nodePath;
244
+ String? _installDir;
245
+ final List<String> _log = [];
246
+ final _logScrollCtrl = ScrollController();
247
+ Process? _proc;
248
+ String? _errorMsg;
249
+
250
+ String get _home => Platform.isWindows
251
+ ? (Platform.environment['USERPROFILE'] ?? '')
252
+ : (Platform.environment['HOME'] ?? '');
253
+
254
+ @override
255
+ void initState() {
256
+ super.initState();
257
+ _check();
258
+ }
259
+
260
+ @override
261
+ void dispose() {
262
+ _logScrollCtrl.dispose();
263
+ _proc?.kill(ProcessSignal.sigkill);
264
+ super.dispose();
265
+ }
266
+
267
+ Future<void> _check() async {
268
+ if (!mounted) return;
269
+ setState(() => _phase = _LocalInstallPhase.checking);
270
+
271
+ final r = await Process.run(Platform.isWindows ? 'where' : 'which', ['node']);
272
+ if (r.exitCode == 0) {
273
+ _nodePath = (r.stdout as String).trim().split('\n').first.trim();
274
+ } else if (!Platform.isWindows) {
275
+ for (final p in ['/opt/homebrew/bin/node', '/usr/local/bin/node']) {
276
+ if (File(p).existsSync()) {
277
+ _nodePath = p;
278
+ break;
279
+ }
280
+ }
281
+ }
282
+
283
+ final defaultDir = '$_home/NeoAgent';
284
+ if (File('$defaultDir/bin/neoagent.js').existsSync()) {
285
+ _installDir = defaultDir;
286
+ }
287
+
288
+ if (!mounted) return;
289
+ if (_nodePath != null && _installDir != null) {
290
+ setState(() => _phase = _LocalInstallPhase.ready);
291
+ } else {
292
+ setState(() {
293
+ _phase = _LocalInstallPhase.failed;
294
+ _errorMsg = _nodePath == null
295
+ ? 'Node.js not found. Install it from nodejs.org then retry.'
296
+ : 'NeoAgent not found at ~/NeoAgent. Run: npm install -g neoagent && neoagent install';
297
+ });
298
+ }
299
+ }
300
+
301
+ bool _ensureEnvFile() {
302
+ final envPath = '$_home/.neoagent/runtime/.env';
303
+ if (!File(envPath).existsSync()) {
304
+ try {
305
+ Directory('$_home/.neoagent/runtime').createSync(recursive: true);
306
+ File(envPath).writeAsStringSync('NODE_ENV=production\nPORT=3333\n');
307
+ } catch (_) {
308
+ return false;
309
+ }
310
+ }
311
+ return true;
312
+ }
313
+
314
+ Future<void> _install() async {
315
+ if (!_ensureEnvFile()) {
316
+ setState(() {
317
+ _phase = _LocalInstallPhase.failed;
318
+ _errorMsg = 'Could not create ~/.neoagent/runtime/.env — check directory permissions.';
319
+ });
320
+ return;
321
+ }
322
+ setState(() {
323
+ _phase = _LocalInstallPhase.installing;
324
+ _log.clear();
325
+ });
326
+
327
+ final process = await Process.start(
328
+ _nodePath!,
329
+ ['bin/neoagent.js', 'install'],
330
+ workingDirectory: _installDir,
331
+ );
332
+ _proc = process;
333
+
334
+ void onChunk(String data) {
335
+ if (!mounted) return;
336
+ setState(() => _log.add(data));
337
+ WidgetsBinding.instance.addPostFrameCallback((_) {
338
+ if (_logScrollCtrl.hasClients) {
339
+ _logScrollCtrl.jumpTo(_logScrollCtrl.position.maxScrollExtent);
340
+ }
341
+ });
342
+ }
343
+
344
+ process.stdout.transform(utf8.decoder).listen(onChunk);
345
+ process.stderr.transform(utf8.decoder).listen(onChunk);
346
+
347
+ final exit = await process.exitCode;
348
+ _proc = null;
349
+ if (!mounted) return;
350
+ setState(() {
351
+ _phase = exit == 0 ? _LocalInstallPhase.done : _LocalInstallPhase.failed;
352
+ if (exit != 0) _errorMsg = 'Installation failed (exit $exit). Check the log above.';
353
+ });
354
+ }
355
+
356
+ @override
357
+ Widget build(BuildContext context) {
358
+ return _AmbientBackdrop(
359
+ child: Scaffold(
360
+ backgroundColor: Colors.transparent,
361
+ body: SafeArea(
362
+ child: Center(
363
+ child: SingleChildScrollView(
364
+ padding: const EdgeInsets.all(24),
365
+ child: ConstrainedBox(
366
+ constraints: const BoxConstraints(maxWidth: 560),
367
+ child: _EntranceMotion(
368
+ child: _GlassSurface(
369
+ borderRadius: BorderRadius.circular(34),
370
+ blurSigma: 28,
371
+ boxShadow: _softPanelShadow,
372
+ overlayGradient: _panelGradient,
373
+ fillColor: _glassFill,
374
+ child: Padding(
375
+ padding: const EdgeInsets.fromLTRB(34, 28, 34, 30),
376
+ child: Column(
377
+ crossAxisAlignment: CrossAxisAlignment.start,
378
+ children: <Widget>[
379
+ TextButton.icon(
380
+ onPressed: widget.onBack,
381
+ icon: const Icon(Icons.arrow_back, size: 16),
382
+ label: const Text('Back'),
383
+ style: TextButton.styleFrom(
384
+ padding: EdgeInsets.zero,
385
+ tapTargetSize: MaterialTapTargetSize.shrinkWrap,
386
+ ),
387
+ ),
388
+ const SizedBox(height: 16),
389
+ const _BrandLockup(logoSize: 60),
390
+ const SizedBox(height: 22),
391
+ Text('INSTALL LOCALLY', style: _sectionEyebrowStyle()),
392
+ const SizedBox(height: 10),
393
+ Text(
394
+ 'Install NeoAgent as a background service',
395
+ style: _displayTitleStyle(28),
396
+ ),
397
+ const SizedBox(height: 20),
398
+ _buildContent(),
198
399
  ],
199
400
  ),
200
401
  ),
@@ -207,6 +408,109 @@ class _BackendSetupViewState extends State<BackendSetupView> {
207
408
  ),
208
409
  );
209
410
  }
411
+
412
+ Widget _buildContent() {
413
+ switch (_phase) {
414
+ case _LocalInstallPhase.checking:
415
+ return const Center(
416
+ child: Padding(
417
+ padding: EdgeInsets.all(24),
418
+ child: CircularProgressIndicator(),
419
+ ),
420
+ );
421
+ case _LocalInstallPhase.ready:
422
+ return Column(
423
+ crossAxisAlignment: CrossAxisAlignment.start,
424
+ children: <Widget>[
425
+ Text(
426
+ 'NeoAgent found at $_installDir. Click Install to set up the background service.',
427
+ style: TextStyle(color: _textSecondary, height: 1.55),
428
+ ),
429
+ const SizedBox(height: 20),
430
+ SizedBox(
431
+ width: double.infinity,
432
+ child: FilledButton.icon(
433
+ onPressed: _install,
434
+ style: FilledButton.styleFrom(
435
+ backgroundColor: _accent,
436
+ padding: const EdgeInsets.symmetric(vertical: 16),
437
+ ),
438
+ icon: const Icon(Icons.download_outlined),
439
+ label: const Text('Install NeoAgent'),
440
+ ),
441
+ ),
442
+ ],
443
+ );
444
+ case _LocalInstallPhase.installing:
445
+ return Column(
446
+ crossAxisAlignment: CrossAxisAlignment.start,
447
+ children: <Widget>[
448
+ Row(
449
+ children: <Widget>[
450
+ const SizedBox(
451
+ width: 18,
452
+ height: 18,
453
+ child: CircularProgressIndicator(strokeWidth: 2),
454
+ ),
455
+ const SizedBox(width: 12),
456
+ const Expanded(child: Text('Installing...')),
457
+ TextButton(
458
+ onPressed: () async {
459
+ final proc = _proc;
460
+ proc?.kill(ProcessSignal.sigterm);
461
+ await proc?.exitCode;
462
+ if (mounted) setState(() => _phase = _LocalInstallPhase.ready);
463
+ },
464
+ child: const Text('Cancel'),
465
+ ),
466
+ ],
467
+ ),
468
+ const SizedBox(height: 12),
469
+ _InstallLogCard(lines: _log, scrollController: _logScrollCtrl),
470
+ ],
471
+ );
472
+ case _LocalInstallPhase.done:
473
+ return Column(
474
+ crossAxisAlignment: CrossAxisAlignment.start,
475
+ children: <Widget>[
476
+ Row(
477
+ children: <Widget>[
478
+ Icon(Icons.check_circle_outline, color: _success),
479
+ const SizedBox(width: 8),
480
+ const Text('NeoAgent is running.'),
481
+ ],
482
+ ),
483
+ const SizedBox(height: 20),
484
+ SizedBox(
485
+ width: double.infinity,
486
+ child: FilledButton.icon(
487
+ onPressed: () =>
488
+ widget.controller.saveBackendUrl('http://localhost:3333'),
489
+ style: FilledButton.styleFrom(
490
+ backgroundColor: _accent,
491
+ padding: const EdgeInsets.symmetric(vertical: 16),
492
+ ),
493
+ icon: const Icon(Icons.arrow_forward_rounded),
494
+ label: const Text('Connect to local NeoAgent'),
495
+ ),
496
+ ),
497
+ ],
498
+ );
499
+ case _LocalInstallPhase.failed:
500
+ return Column(
501
+ crossAxisAlignment: CrossAxisAlignment.start,
502
+ children: <Widget>[
503
+ _InlineError(message: _errorMsg ?? 'Installation failed.'),
504
+ const SizedBox(height: 16),
505
+ OutlinedButton.icon(
506
+ onPressed: _check,
507
+ icon: const Icon(Icons.refresh),
508
+ label: const Text('Retry'),
509
+ ),
510
+ ],
511
+ );
512
+ }
513
+ }
210
514
  }
211
515
 
212
516
  class AuthView extends StatefulWidget {
@@ -1391,6 +1695,7 @@ class _HomeViewState extends State<HomeView> {
1391
1695
  .allowMessagingSuggestion(
1392
1696
  notice.platform,
1393
1697
  suggestion,
1698
+ chatId: notice.chatId,
1394
1699
  );
1395
1700
  },
1396
1701
  icon: Icon(Icons.verified_user_outlined),
@@ -1412,6 +1717,13 @@ class _HomeViewState extends State<HomeView> {
1412
1717
  },
1413
1718
  child: Text('Open Messaging'),
1414
1719
  ),
1720
+ TextButton(
1721
+ onPressed: () async {
1722
+ Navigator.of(dialogContext).pop();
1723
+ await widget.controller.ignoreBlockedSender(notice);
1724
+ },
1725
+ child: Text('Ignore'),
1726
+ ),
1415
1727
  TextButton(
1416
1728
  onPressed: () => Navigator.of(dialogContext).pop(),
1417
1729
  child: Text('Dismiss'),
@@ -2216,6 +2528,10 @@ class _SectionBody extends StatelessWidget {
2216
2528
  return controller.showHealthSection
2217
2529
  ? HealthPanel(controller: controller)
2218
2530
  : ChatPanel(controller: controller);
2531
+ case AppSection.server:
2532
+ return ServerPanel(controller: controller);
2533
+ case AppSection.billing:
2534
+ return BillingPanel(controller: controller);
2219
2535
  }
2220
2536
  }
2221
2537
  }