neoagent 2.3.1-beta.99 → 2.4.1-beta.5

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 (76) hide show
  1. package/.env.example +6 -3
  2. package/LICENSE +619 -21
  3. package/README.md +1 -1
  4. package/extensions/chrome-browser/icons/icon128.png +0 -0
  5. package/extensions/chrome-browser/icons/icon16.png +0 -0
  6. package/extensions/chrome-browser/icons/icon48.png +0 -0
  7. package/extensions/chrome-browser/icons/logo.svg +12 -0
  8. package/extensions/chrome-browser/manifest.json +11 -1
  9. package/extensions/chrome-browser/popup.css +5 -0
  10. package/extensions/chrome-browser/popup.html +2 -4
  11. package/extensions/chrome-browser/popup.js +1 -5
  12. package/flutter_app/lib/main.dart +1 -0
  13. package/flutter_app/lib/main_controller.dart +9 -0
  14. package/flutter_app/lib/main_devices.dart +70 -1
  15. package/flutter_app/lib/main_integrations.dart +21 -2
  16. package/flutter_app/lib/main_models.dart +60 -0
  17. package/flutter_app/lib/main_settings.dart +172 -31
  18. package/flutter_app/lib/main_theme.dart +31 -2
  19. package/flutter_app/lib/src/backend_client.dart +12 -0
  20. package/flutter_app/lib/src/desktop_companion_actions.dart +72 -0
  21. package/flutter_app/lib/src/desktop_companion_io.dart +9 -4
  22. package/flutter_app/macos/Runner/AppDelegate.swift +23 -2
  23. package/flutter_app/macos/Runner/DebugProfile.entitlements +4 -0
  24. package/flutter_app/macos/Runner/Release.entitlements +4 -0
  25. package/flutter_app/pubspec.lock +5 -5
  26. package/lib/manager.js +164 -2
  27. package/package.json +2 -2
  28. package/server/db/database.js +85 -0
  29. package/server/guest_agent.js +12 -1
  30. package/server/http/routes.js +190 -0
  31. package/server/public/.last_build_id +1 -1
  32. package/server/public/assets/NOTICES +971 -1066
  33. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  34. package/server/public/assets/shaders/ink_sparkle.frag +1 -1
  35. package/server/public/assets/shaders/stretch_effect.frag +1 -1
  36. package/server/public/canvaskit/canvaskit.js +2 -2
  37. package/server/public/canvaskit/canvaskit.js.symbols +11796 -11733
  38. package/server/public/canvaskit/canvaskit.wasm +0 -0
  39. package/server/public/canvaskit/chromium/canvaskit.js +2 -2
  40. package/server/public/canvaskit/chromium/canvaskit.js.symbols +10706 -10643
  41. package/server/public/canvaskit/chromium/canvaskit.wasm +0 -0
  42. package/server/public/canvaskit/experimental_webparagraph/canvaskit.js +171 -0
  43. package/server/public/canvaskit/experimental_webparagraph/canvaskit.js.symbols +9134 -0
  44. package/server/public/canvaskit/experimental_webparagraph/canvaskit.wasm +0 -0
  45. package/server/public/canvaskit/skwasm.js +14 -14
  46. package/server/public/canvaskit/skwasm.js.symbols +12787 -12676
  47. package/server/public/canvaskit/skwasm.wasm +0 -0
  48. package/server/public/canvaskit/skwasm_heavy.js +14 -14
  49. package/server/public/canvaskit/skwasm_heavy.js.symbols +14400 -14286
  50. package/server/public/canvaskit/skwasm_heavy.wasm +0 -0
  51. package/server/public/canvaskit/wimp.js +94 -95
  52. package/server/public/canvaskit/wimp.js.symbols +11325 -11177
  53. package/server/public/canvaskit/wimp.wasm +0 -0
  54. package/server/public/flutter_bootstrap.js +2 -2
  55. package/server/public/main.dart.js +84332 -82269
  56. package/server/routes/integrations.js +2 -2
  57. package/server/routes/memory.js +73 -0
  58. package/server/services/ai/engine.js +65 -26
  59. package/server/services/ai/models.js +21 -0
  60. package/server/services/ai/preModelCompaction.js +191 -0
  61. package/server/services/ai/providers/claudeCode.js +273 -0
  62. package/server/services/ai/providers/openaiCodex.js +212 -40
  63. package/server/services/ai/settings.js +12 -2
  64. package/server/services/ai/tools.js +39 -28
  65. package/server/services/desktop/protocol.js +1 -0
  66. package/server/services/desktop/provider.js +11 -0
  67. package/server/services/desktop/registry.js +51 -10
  68. package/server/services/integrations/google/provider.js +78 -0
  69. package/server/services/integrations/manager.js +29 -13
  70. package/server/services/manager.js +25 -0
  71. package/server/services/memory/ingestion.js +486 -0
  72. package/server/services/memory/manager.js +422 -0
  73. package/server/services/memory/openhuman_uplift.test.js +98 -0
  74. package/server/services/runtime/docker-vm-manager.js +26 -3
  75. package/server/services/runtime/manager.js +25 -2
  76. package/server/services/widgets/focus_widget.js +45 -4
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node.js-20+-5fa04e?style=flat-square&logo=node.js&logoColor=white" alt="Node.js"></a>
7
7
  <a href="https://sqlite.org"><img src="https://img.shields.io/badge/SQLite-WAL-003b57?style=flat-square&logo=sqlite&logoColor=white" alt="SQLite"></a>
8
8
  <a href="https://flutter.dev"><img src="https://img.shields.io/badge/Flutter-web%20%2B%20android-02569B?style=flat-square&logo=flutter&logoColor=white" alt="Flutter"></a>
9
- <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-a855f7?style=flat-square" alt="License"></a>
9
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-AGPL--3.0-a855f7?style=flat-square" alt="License"></a>
10
10
  </p>
11
11
 
12
12
  <p align="center">Self-hosted AI agent — runs as a system service, controls Android over ADB, connects to 15+ messaging platforms, all credentials on your server.</p>
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#8f6d3e"/>
5
+ <stop offset="100%" stop-color="#2f7d6e"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect x="5.76" y="5.76" width="20.48" height="20.48" rx="6.96" fill="url(#bg)" stroke="#ffffff" stroke-opacity="0.16" stroke-width="1"/>
9
+ <polygon points="16,9.76 9.35,13.12 16,16.48 22.65,13.12" fill="white"/>
10
+ <polyline points="9.35,16.48 16,19.68 22.65,16.48" fill="none" stroke="white" stroke-width="1.44" stroke-linecap="round" stroke-linejoin="round"/>
11
+ <polyline points="9.35,19.68 16,22.72 22.65,19.68" fill="none" stroke="white" stroke-width="1.44" stroke-linecap="round" stroke-linejoin="round"/>
12
+ </svg>
@@ -6,9 +6,19 @@
6
6
  "minimum_chrome_version": "118",
7
7
  "permissions": ["debugger", "storage", "tabs"],
8
8
  "host_permissions": ["http://*/*", "https://*/*"],
9
+ "icons": {
10
+ "16": "icons/icon16.png",
11
+ "48": "icons/icon48.png",
12
+ "128": "icons/icon128.png"
13
+ },
9
14
  "action": {
10
15
  "default_title": "NeoAgent Browser",
11
- "default_popup": "popup.html"
16
+ "default_popup": "popup.html",
17
+ "default_icon": {
18
+ "16": "icons/icon16.png",
19
+ "48": "icons/icon48.png",
20
+ "128": "icons/icon128.png"
21
+ }
12
22
  },
13
23
  "background": {
14
24
  "service_worker": "background.mjs",
@@ -46,6 +46,11 @@ header {
46
46
  gap: 6px;
47
47
  }
48
48
 
49
+ .logo {
50
+ width: 40px;
51
+ height: 40px;
52
+ }
53
+
49
54
  h1,
50
55
  h2,
51
56
  p {
@@ -8,6 +8,7 @@
8
8
  <body>
9
9
  <main>
10
10
  <header>
11
+ <img src="icons/logo.svg" alt="NeoAgent" class="logo">
11
12
  <p class="eyebrow">NeoAgent Browser</p>
12
13
  <h1>Connect this Chrome</h1>
13
14
  <p class="intro">Pair once, then let NeoAgent control this browser when you ask it to.</p>
@@ -31,8 +32,6 @@
31
32
  </div>
32
33
  </section>
33
34
 
34
- <button id="openApp" type="button" class="link-button">Open NeoAgent web page</button>
35
-
36
35
  <details id="settings" class="settings">
37
36
  <summary>Settings &amp; updates</summary>
38
37
  <label>
@@ -47,8 +46,7 @@
47
46
  <p class="hint">The server URL is usually filled in by the ZIP downloaded from NeoAgent.</p>
48
47
  </details>
49
48
 
50
- <p class="hint">Pairing opens NeoAgent in a tab. This extension never stores your NeoAgent password.</p>
51
- <p id="message" class="message"></p>
49
+ <p id="message" class="message"></p>
52
50
  </main>
53
51
  <script src="popup.js" type="module"></script>
54
52
  </body>
@@ -9,7 +9,6 @@ const flowTitleEl = document.querySelector('#flowTitle');
9
9
  const flowDescriptionEl = document.querySelector('#flowDescription');
10
10
  const primaryActionEl = document.querySelector('#primaryAction');
11
11
  const secondaryActionEl = document.querySelector('#secondaryAction');
12
- const openAppEl = document.querySelector('#openApp');
13
12
  const disconnectEl = document.querySelector('#disconnect');
14
13
  const checkUpdateEl = document.querySelector('#checkUpdate');
15
14
  const downloadEl = document.querySelector('#download');
@@ -59,7 +58,7 @@ function setBusy(isBusy, label = 'Working...') {
59
58
  }
60
59
  const busy = pendingActions > 0;
61
60
 
62
- [primaryActionEl, secondaryActionEl, openAppEl, disconnectEl, checkUpdateEl, downloadEl].forEach((button) => {
61
+ [primaryActionEl, secondaryActionEl, disconnectEl, checkUpdateEl, downloadEl].forEach((button) => {
63
62
  if (!button || button.hidden) return;
64
63
  if (busy) {
65
64
  if (!Object.prototype.hasOwnProperty.call(button.dataset, 'wasDisabled')) {
@@ -99,8 +98,6 @@ function updateFlow() {
99
98
  const hasToken = Boolean(currentState.token || currentState.tokenId);
100
99
  const approvalUrl = currentState.approvalUrl || '';
101
100
 
102
- openAppEl.disabled = !hasServerUrl;
103
-
104
101
  if (!hasServerUrl) {
105
102
  stepLabelEl.textContent = 'Step 1 of 3';
106
103
  flowTitleEl.textContent = 'Add your NeoAgent server';
@@ -243,7 +240,6 @@ serverUrlEl.addEventListener('input', updateFlow);
243
240
 
244
241
  bindAsyncClick(primaryActionEl, () => runAction(primaryActionEl.dataset.action));
245
242
  bindAsyncClick(secondaryActionEl, () => runAction(secondaryActionEl.dataset.action));
246
- bindAsyncClick(openAppEl, () => runAction('openApp'));
247
243
  bindAsyncClick(disconnectEl, async () => {
248
244
  await send('disconnect');
249
245
  await refresh();
@@ -6,6 +6,7 @@ import 'dart:ui' show ImageFilter;
6
6
 
7
7
  import 'package:connectivity_plus/connectivity_plus.dart';
8
8
  import 'package:hotkey_manager/hotkey_manager.dart';
9
+ import 'package:flutter/cupertino.dart' as cupertino;
9
10
  import 'package:flutter/foundation.dart';
10
11
  import 'package:flutter/gestures.dart';
11
12
  import 'package:flutter/material.dart';
@@ -1040,6 +1040,15 @@ class NeoAgentController extends ChangeNotifier {
1040
1040
  }
1041
1041
  }
1042
1042
 
1043
+ Future<Map<String, dynamic>> testCliRuntime() =>
1044
+ _backendClient.testCli(backendUrl);
1045
+
1046
+ Future<Map<String, dynamic>> testBrowserExtension() =>
1047
+ _backendClient.testExtension(backendUrl);
1048
+
1049
+ Future<Map<String, dynamic>> testDesktopCompanion() =>
1050
+ _backendClient.testDesktop(backendUrl);
1051
+
1043
1052
  Future<void> openAppUpdate() async {
1044
1053
  final release = availableAppUpdate;
1045
1054
  if (release == null || isOpeningAppUpdate) {
@@ -103,8 +103,13 @@ class _DevicesPanelState extends State<DevicesPanel> {
103
103
  _onlineDesktopDevices.length > 1 &&
104
104
  (widget.controller.selectedDesktopDeviceId ?? '').isEmpty;
105
105
 
106
+ bool get _extensionPreferredButOffline =>
107
+ widget.controller.browserBackend == 'extension' &&
108
+ !widget.controller.browserExtensionConnected;
109
+
106
110
  String? get _activeScreenshotPath {
107
111
  if (_isBrowser) {
112
+ if (_extensionPreferredButOffline) return null;
108
113
  return widget.controller.browserScreenshotPath;
109
114
  }
110
115
  if (_isDesktop) {
@@ -121,6 +126,7 @@ class _DevicesPanelState extends State<DevicesPanel> {
121
126
  Future<void> _ensurePreview() async {
122
127
  final controller = widget.controller;
123
128
  if (_isBrowser) {
129
+ if (_extensionPreferredButOffline) return;
124
130
  if (controller.browserRuntime['launched'] != true) {
125
131
  return;
126
132
  }
@@ -161,6 +167,7 @@ class _DevicesPanelState extends State<DevicesPanel> {
161
167
  return;
162
168
  }
163
169
  if (_isBrowser) {
170
+ if (_extensionPreferredButOffline) return;
164
171
  await widget.controller.refreshBrowserFrameRuntime();
165
172
  return;
166
173
  }
@@ -451,6 +458,14 @@ class _DevicesPanelState extends State<DevicesPanel> {
451
458
  browserExtensionActive: usingExtension,
452
459
  browserFallbackLabel: browserFallbackLabel,
453
460
  ),
461
+ if (_isBrowser && prefersExtension) ...<Widget>[
462
+ const SizedBox(height: 14),
463
+ _ExtensionStatusBar(
464
+ connected: extensionConnected,
465
+ onDownload: controller.downloadBrowserExtension,
466
+ onRefresh: controller.refreshBrowserExtensionStatus,
467
+ ),
468
+ ],
454
469
  if (_isDesktop) ...<Widget>[
455
470
  const SizedBox(height: 14),
456
471
  DropdownButtonFormField<String>(
@@ -558,7 +573,9 @@ class _DevicesPanelState extends State<DevicesPanel> {
558
573
  busy: _isCurrentSurfaceBusy,
559
574
  wakingUp: !_isBrowser && !_isDesktop && _androidStarting,
560
575
  enabled: _isBrowser || _isDesktop || _androidOnline,
561
- connectRequired: _desktopRequiresSelection,
576
+ connectRequired: _isBrowser
577
+ ? _extensionPreferredButOffline
578
+ : _desktopRequiresSelection,
562
579
  onTapPoint: _handleTap,
563
580
  onSwipe: _handleSwipe,
564
581
  onWakeRequested: _openPrimary,
@@ -1701,6 +1718,58 @@ class _RuntimePreview extends StatelessWidget {
1701
1718
  }
1702
1719
  }
1703
1720
 
1721
+ class _ExtensionStatusBar extends StatelessWidget {
1722
+ const _ExtensionStatusBar({
1723
+ required this.connected,
1724
+ required this.onDownload,
1725
+ required this.onRefresh,
1726
+ });
1727
+
1728
+ final bool connected;
1729
+ final Future<void> Function() onDownload;
1730
+ final Future<void> Function() onRefresh;
1731
+
1732
+ @override
1733
+ Widget build(BuildContext context) {
1734
+ return Container(
1735
+ padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
1736
+ decoration: BoxDecoration(
1737
+ color: _bgSecondary,
1738
+ borderRadius: BorderRadius.circular(16),
1739
+ border: Border.all(color: _borderLight),
1740
+ ),
1741
+ child: Row(
1742
+ children: <Widget>[
1743
+ _DotStatus(
1744
+ label: connected ? 'Extension connected' : 'Extension not connected',
1745
+ color: connected ? _success : _warning,
1746
+ ),
1747
+ const Spacer(),
1748
+ OutlinedButton.icon(
1749
+ onPressed: onDownload,
1750
+ icon: const Icon(Icons.download_outlined, size: 18),
1751
+ label: const Text('Download'),
1752
+ style: OutlinedButton.styleFrom(
1753
+ visualDensity: VisualDensity.compact,
1754
+ padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
1755
+ ),
1756
+ ),
1757
+ const SizedBox(width: 8),
1758
+ OutlinedButton.icon(
1759
+ onPressed: onRefresh,
1760
+ icon: const Icon(Icons.sync, size: 18),
1761
+ label: const Text('Refresh'),
1762
+ style: OutlinedButton.styleFrom(
1763
+ visualDensity: VisualDensity.compact,
1764
+ padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
1765
+ ),
1766
+ ),
1767
+ ],
1768
+ ),
1769
+ );
1770
+ }
1771
+ }
1772
+
1704
1773
  class _ResultBlock extends StatelessWidget {
1705
1774
  const _ResultBlock({required this.label, required this.value});
1706
1775
 
@@ -175,6 +175,12 @@ class OfficialIntegrationsTab extends StatelessWidget {
175
175
  label: '${item.availableToolCount} tools',
176
176
  icon: Icons.build_outlined,
177
177
  ),
178
+ _MetaPill(
179
+ label: item.memoryCoverage.supported
180
+ ? 'Memory ${item.memoryCoverage.statusLabel}'
181
+ : 'No memory sync',
182
+ icon: Icons.psychology_alt_outlined,
183
+ ),
178
184
  ],
179
185
  ),
180
186
  const SizedBox(height: 10),
@@ -255,6 +261,7 @@ Future<void> _showTrelloSetupDialog(
255
261
  final apiKeyController = TextEditingController(text: savedApiKey);
256
262
  final tokenInputController = TextEditingController();
257
263
 
264
+ if (!context.mounted) return;
258
265
  await showDialog<void>(
259
266
  context: context,
260
267
  barrierDismissible: false,
@@ -426,8 +433,7 @@ Future<void> _showTrelloSetupDialog(
426
433
  }
427
434
  final url = authorizeUrl.isNotEmpty
428
435
  ? authorizeUrl
429
- : 'https://trello.com/1/authorize?expiration=never&scope=read,write,account&response_type=token&key=' +
430
- Uri.encodeComponent(effectiveApiKey);
436
+ : 'https://trello.com/1/authorize?expiration=never&scope=read,write,account&response_type=token&key=${Uri.encodeComponent(effectiveApiKey)}';
431
437
  final result = await controller._oauthLauncher
432
438
  .openExternal(url: url, label: 'Trello');
433
439
  if (!result.launched) {
@@ -659,6 +665,12 @@ class _OfficialIntegrationAppCard extends StatelessWidget {
659
665
  label: '${app.availableToolCount} tools',
660
666
  icon: Icons.build_circle_outlined,
661
667
  ),
668
+ _MetaPill(
669
+ label: app.memoryCoverage.supported
670
+ ? 'Memory ${app.memoryCoverage.statusLabel}'
671
+ : 'No memory sync',
672
+ icon: Icons.psychology_alt_outlined,
673
+ ),
662
674
  ],
663
675
  ),
664
676
  ],
@@ -715,6 +727,13 @@ class _OfficialIntegrationAppCard extends StatelessWidget {
715
727
  'Access: ${account.accessModeLabel}',
716
728
  style: TextStyle(color: _textSecondary),
717
729
  ),
730
+ if (account.memoryCoverage.supported) ...<Widget>[
731
+ const SizedBox(height: 4),
732
+ Text(
733
+ 'Memory: ${account.memoryCoverage.statusLabel}',
734
+ style: TextStyle(color: _textSecondary),
735
+ ),
736
+ ],
718
737
  const SizedBox(height: 12),
719
738
  Wrap(
720
739
  spacing: 8,
@@ -2565,6 +2565,7 @@ class OfficialIntegrationAppItem {
2565
2565
  ),
2566
2566
  this.accounts = const <OfficialIntegrationAccountItem>[],
2567
2567
  this.availableToolCount = 0,
2568
+ this.memoryCoverage = const OfficialIntegrationMemoryCoverage(),
2568
2569
  });
2569
2570
 
2570
2571
  factory OfficialIntegrationAppItem.fromJson(Map<dynamic, dynamic> json) {
@@ -2583,6 +2584,9 @@ class OfficialIntegrationAppItem {
2583
2584
  .toList()
2584
2585
  : const <OfficialIntegrationAccountItem>[],
2585
2586
  availableToolCount: _asInt(json['availableToolCount']),
2587
+ memoryCoverage: OfficialIntegrationMemoryCoverage.fromJson(
2588
+ _jsonMap(json['memoryCoverage']),
2589
+ ),
2586
2590
  );
2587
2591
  }
2588
2592
 
@@ -2592,6 +2596,7 @@ class OfficialIntegrationAppItem {
2592
2596
  final OfficialIntegrationConnectionStatus connection;
2593
2597
  final List<OfficialIntegrationAccountItem> accounts;
2594
2598
  final int availableToolCount;
2599
+ final OfficialIntegrationMemoryCoverage memoryCoverage;
2595
2600
 
2596
2601
  bool get isConnected => connection.connected;
2597
2602
 
@@ -2676,6 +2681,51 @@ class OfficialIntegrationConnectionStatus {
2676
2681
  }
2677
2682
  }
2678
2683
 
2684
+ class OfficialIntegrationMemoryCoverage {
2685
+ const OfficialIntegrationMemoryCoverage({
2686
+ this.supported = false,
2687
+ this.contributesToMemory = false,
2688
+ this.contributesToTaskExecution = false,
2689
+ this.status = 'not_supported',
2690
+ this.dataDomains = const <String>[],
2691
+ this.documentCount = 0,
2692
+ this.lastRefreshAt,
2693
+ this.nextRefreshAt,
2694
+ this.error,
2695
+ });
2696
+
2697
+ factory OfficialIntegrationMemoryCoverage.fromJson(
2698
+ Map<dynamic, dynamic> json,
2699
+ ) {
2700
+ final domainsRaw = json['dataDomains'];
2701
+ return OfficialIntegrationMemoryCoverage(
2702
+ supported: json['supported'] == true,
2703
+ contributesToMemory: json['contributesToMemory'] == true,
2704
+ contributesToTaskExecution: json['contributesToTaskExecution'] == true,
2705
+ status: json['status']?.toString() ?? 'not_supported',
2706
+ dataDomains: domainsRaw is List
2707
+ ? domainsRaw.map((item) => item.toString()).toList()
2708
+ : const <String>[],
2709
+ documentCount: _asInt(json['documentCount']),
2710
+ lastRefreshAt: _parseOptionalTimestamp(json['lastRefreshAt']?.toString()),
2711
+ nextRefreshAt: _parseOptionalTimestamp(json['nextRefreshAt']?.toString()),
2712
+ error: json['error']?.toString(),
2713
+ );
2714
+ }
2715
+
2716
+ final bool supported;
2717
+ final bool contributesToMemory;
2718
+ final bool contributesToTaskExecution;
2719
+ final String status;
2720
+ final List<String> dataDomains;
2721
+ final int documentCount;
2722
+ final DateTime? lastRefreshAt;
2723
+ final DateTime? nextRefreshAt;
2724
+ final String? error;
2725
+
2726
+ String get statusLabel => _titleCase(status.replaceAll('_', ' '));
2727
+ }
2728
+
2679
2729
  class OfficialIntegrationAccountItem {
2680
2730
  const OfficialIntegrationAccountItem({
2681
2731
  required this.id,
@@ -2684,6 +2734,7 @@ class OfficialIntegrationAccountItem {
2684
2734
  this.accountEmail,
2685
2735
  this.lastConnectedAt,
2686
2736
  this.accessMode = 'read_write',
2737
+ this.memoryCoverage = const OfficialIntegrationMemoryCoverage(),
2687
2738
  });
2688
2739
 
2689
2740
  factory OfficialIntegrationAccountItem.fromJson(Map<dynamic, dynamic> json) {
@@ -2696,6 +2747,9 @@ class OfficialIntegrationAccountItem {
2696
2747
  json['lastConnectedAt']?.toString(),
2697
2748
  ),
2698
2749
  accessMode: json['accessMode']?.toString() ?? 'read_write',
2750
+ memoryCoverage: OfficialIntegrationMemoryCoverage.fromJson(
2751
+ _jsonMap(json['memoryCoverage']),
2752
+ ),
2699
2753
  );
2700
2754
  }
2701
2755
 
@@ -2705,6 +2759,7 @@ class OfficialIntegrationAccountItem {
2705
2759
  final String? accountEmail;
2706
2760
  final DateTime? lastConnectedAt;
2707
2761
  final String accessMode;
2762
+ final OfficialIntegrationMemoryCoverage memoryCoverage;
2708
2763
 
2709
2764
  bool get isExpired => status == 'expired';
2710
2765
 
@@ -2733,6 +2788,7 @@ class OfficialIntegrationItem {
2733
2788
  this.connectPrompt,
2734
2789
  this.supportsMultipleAccounts = true,
2735
2790
  this.connectionMethod = 'oauth',
2791
+ this.memoryCoverage = const OfficialIntegrationMemoryCoverage(),
2736
2792
  });
2737
2793
 
2738
2794
  factory OfficialIntegrationItem.fromJson(Map<dynamic, dynamic> json) {
@@ -2756,6 +2812,9 @@ class OfficialIntegrationItem {
2756
2812
  connectPrompt: json['connectPrompt']?.toString(),
2757
2813
  supportsMultipleAccounts: json['supportsMultipleAccounts'] != false,
2758
2814
  connectionMethod: json['connectionMethod']?.toString() ?? 'oauth',
2815
+ memoryCoverage: OfficialIntegrationMemoryCoverage.fromJson(
2816
+ _jsonMap(json['memoryCoverage']),
2817
+ ),
2759
2818
  );
2760
2819
  }
2761
2820
 
@@ -2770,6 +2829,7 @@ class OfficialIntegrationItem {
2770
2829
  final String? connectPrompt;
2771
2830
  final bool supportsMultipleAccounts;
2772
2831
  final String connectionMethod;
2832
+ final OfficialIntegrationMemoryCoverage memoryCoverage;
2773
2833
 
2774
2834
  bool get isConnected => connection.connected;
2775
2835