neoagent 2.3.1-beta.85 → 2.3.1-beta.87

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 (55) hide show
  1. package/docs/capabilities.md +2 -0
  2. package/flutter_app/android/app/src/main/AndroidManifest.xml +14 -0
  3. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +84 -0
  4. package/flutter_app/lib/main_chat.dart +156 -2
  5. package/flutter_app/lib/main_controller.dart +137 -10
  6. package/flutter_app/lib/main_models.dart +69 -0
  7. package/flutter_app/lib/main_operations.dart +248 -0
  8. package/flutter_app/lib/main_runtime.dart +11 -2
  9. package/flutter_app/lib/main_settings.dart +173 -176
  10. package/flutter_app/lib/main_shared.dart +78 -0
  11. package/flutter_app/lib/src/app_launch_bridge.dart +39 -10
  12. package/flutter_app/lib/src/backend_client.dart +28 -0
  13. package/package.json +1 -1
  14. package/server/guest-agent.android.package.json +13 -0
  15. package/server/guest-agent.browser.package.json +14 -0
  16. package/server/guest_agent.js +61 -44
  17. package/server/http/routes.js +1 -0
  18. package/server/public/.last_build_id +1 -1
  19. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  20. package/server/public/flutter_bootstrap.js +1 -1
  21. package/server/public/main.dart.js +69936 -69277
  22. package/server/routes/android.js +2 -11
  23. package/server/routes/browser.js +2 -2
  24. package/server/routes/memory.js +90 -0
  25. package/server/routes/social_video.js +62 -0
  26. package/server/services/ai/capabilityHealth.js +6 -14
  27. package/server/services/ai/systemPrompt.js +1 -0
  28. package/server/services/ai/toolResult.js +20 -0
  29. package/server/services/ai/tools.js +29 -0
  30. package/server/services/android/android_bootstrap_worker.js +2 -2
  31. package/server/services/android/controller.js +528 -132
  32. package/server/services/browser/controller.js +51 -68
  33. package/server/services/manager.js +15 -0
  34. package/server/services/memory/llm_transfer.js +217 -0
  35. package/server/services/runtime/backends/local-vm.js +16 -3
  36. package/server/services/runtime/guest_bootstrap.js +224 -56
  37. package/server/services/runtime/manager.js +53 -15
  38. package/server/services/runtime/qemu.js +149 -24
  39. package/server/services/runtime/settings.js +9 -14
  40. package/server/services/runtime/validation.js +10 -11
  41. package/server/services/social_video/adapters/base.js +26 -0
  42. package/server/services/social_video/adapters/index.js +27 -0
  43. package/server/services/social_video/adapters/instagram.js +17 -0
  44. package/server/services/social_video/adapters/tiktok.js +17 -0
  45. package/server/services/social_video/adapters/x.js +17 -0
  46. package/server/services/social_video/adapters/youtube.js +17 -0
  47. package/server/services/social_video/captions.js +187 -0
  48. package/server/services/social_video/frame.js +42 -0
  49. package/server/services/social_video/index.js +7 -0
  50. package/server/services/social_video/metadata.js +63 -0
  51. package/server/services/social_video/result.js +63 -0
  52. package/server/services/social_video/service.js +576 -0
  53. package/server/services/social_video/url.js +83 -0
  54. package/server/utils/deployment.js +4 -4
  55. package/server/guest-agent.package.json +0 -15
@@ -598,10 +598,7 @@ class _SettingsPanelState extends State<SettingsPanel> {
598
598
  'Cloud uses the isolated browser runtime. Extension uses a paired Chrome browser on the remote machine.',
599
599
  ),
600
600
  items: const <DropdownMenuItem<String>>[
601
- DropdownMenuItem<String>(
602
- value: 'vm',
603
- child: Text('Cloud'),
604
- ),
601
+ DropdownMenuItem<String>(value: 'vm', child: Text('Cloud')),
605
602
  DropdownMenuItem<String>(
606
603
  value: 'extension',
607
604
  child: Text('Chrome extension'),
@@ -1465,194 +1462,194 @@ class _SettingsPanelState extends State<SettingsPanel> {
1465
1462
  ),
1466
1463
  const SizedBox(height: 12),
1467
1464
  if (!controller.appUpdaterConfigured)
1468
- Text(
1469
- kIsWeb
1470
- ? 'Client app update checks are disabled in the web app to avoid blocked browser-side GitHub requests.'
1471
- : 'Client app updates are not configured for this build.',
1472
- style: TextStyle(color: _textSecondary, height: 1.5),
1473
- )
1474
- else ...<Widget>[
1475
- LayoutBuilder(
1476
- builder: (context, constraints) {
1477
- final compact = constraints.maxWidth < 780;
1478
- final channelPicker = DropdownButtonFormField<String>(
1479
- initialValue: controller.appUpdateChannel,
1480
- decoration: const InputDecoration(
1481
- labelText: 'App release channel',
1482
- ),
1483
- items: const <DropdownMenuItem<String>>[
1484
- DropdownMenuItem<String>(
1485
- value: 'stable',
1486
- child: Text('Stable'),
1465
+ if (!kIsWeb)
1466
+ Text(
1467
+ 'Client app updates are not configured for this build.',
1468
+ style: TextStyle(color: _textSecondary, height: 1.5),
1469
+ )
1470
+ else ...<Widget>[
1471
+ LayoutBuilder(
1472
+ builder: (context, constraints) {
1473
+ final compact = constraints.maxWidth < 780;
1474
+ final channelPicker = DropdownButtonFormField<String>(
1475
+ initialValue: controller.appUpdateChannel,
1476
+ decoration: const InputDecoration(
1477
+ labelText: 'App release channel',
1487
1478
  ),
1488
- DropdownMenuItem<String>(
1489
- value: 'beta',
1490
- child: Text('Beta'),
1479
+ items: const <DropdownMenuItem<String>>[
1480
+ DropdownMenuItem<String>(
1481
+ value: 'stable',
1482
+ child: Text('Stable'),
1483
+ ),
1484
+ DropdownMenuItem<String>(
1485
+ value: 'beta',
1486
+ child: Text('Beta'),
1487
+ ),
1488
+ ],
1489
+ onChanged: (value) {
1490
+ if (value != null) {
1491
+ unawaited(controller.setAppUpdateChannel(value));
1492
+ }
1493
+ },
1494
+ );
1495
+ final autoCheck = SwitchListTile.adaptive(
1496
+ value: controller.appUpdateAutoCheckEnabled,
1497
+ contentPadding: EdgeInsets.zero,
1498
+ title: Text('Check automatically on launch'),
1499
+ subtitle: Text(
1500
+ 'This only checks GitHub Releases on startup. Installation still requires your confirmation.',
1501
+ style: TextStyle(color: _textSecondary),
1491
1502
  ),
1492
- ],
1493
- onChanged: (value) {
1494
- if (value != null) {
1495
- unawaited(controller.setAppUpdateChannel(value));
1496
- }
1497
- },
1498
- );
1499
- final autoCheck = SwitchListTile.adaptive(
1500
- value: controller.appUpdateAutoCheckEnabled,
1501
- contentPadding: EdgeInsets.zero,
1502
- title: Text('Check automatically on launch'),
1503
- subtitle: Text(
1504
- 'This only checks GitHub Releases on startup. Installation still requires your confirmation.',
1505
- style: TextStyle(color: _textSecondary),
1506
- ),
1507
- onChanged: controller.setAppUpdateAutoCheckEnabled,
1508
- );
1503
+ onChanged: controller.setAppUpdateAutoCheckEnabled,
1504
+ );
1509
1505
 
1510
- if (compact) {
1511
- return Column(
1506
+ if (compact) {
1507
+ return Column(
1508
+ crossAxisAlignment: CrossAxisAlignment.start,
1509
+ children: <Widget>[
1510
+ channelPicker,
1511
+ const SizedBox(height: 10),
1512
+ autoCheck,
1513
+ ],
1514
+ );
1515
+ }
1516
+
1517
+ return Row(
1512
1518
  crossAxisAlignment: CrossAxisAlignment.start,
1513
1519
  children: <Widget>[
1514
- channelPicker,
1515
- const SizedBox(height: 10),
1516
- autoCheck,
1520
+ Expanded(child: channelPicker),
1521
+ const SizedBox(width: 16),
1522
+ Expanded(child: autoCheck),
1517
1523
  ],
1518
1524
  );
1519
- }
1520
-
1521
- return Row(
1522
- crossAxisAlignment: CrossAxisAlignment.start,
1523
- children: <Widget>[
1524
- Expanded(child: channelPicker),
1525
- const SizedBox(width: 16),
1526
- Expanded(child: autoCheck),
1527
- ],
1528
- );
1529
- },
1530
- ),
1531
- const SizedBox(height: 8),
1532
- Text(
1533
- 'Installed: ${controller.installedAppVersion ?? 'Unknown'} | Channel: ${controller.appUpdateChannelLabel} | Last checked: ${controller.appUpdateLastCheckedLabel}',
1534
- style: TextStyle(color: _textSecondary),
1535
- ),
1536
- const SizedBox(height: 6),
1537
- Text(
1538
- 'Source: ${app_release_updater.appUpdaterGithubOwner}/${app_release_updater.appUpdaterGithubRepo}${app_release_updater.appUpdaterGithubToken.trim().isNotEmpty ? ' (override active)' : ''}',
1539
- style: TextStyle(color: _textSecondary),
1540
- ),
1541
- if (controller.appUpdateErrorMessage
1542
- case final message?) ...<Widget>[
1543
- const SizedBox(height: 12),
1544
- _InlineError(message: message),
1545
- ],
1546
- if (controller.availableAppUpdate
1547
- case final release?) ...<Widget>[
1548
- const SizedBox(height: 16),
1549
- Container(
1550
- width: double.infinity,
1551
- padding: const EdgeInsets.all(16),
1552
- decoration: BoxDecoration(
1553
- color: _bgSecondary,
1554
- borderRadius: BorderRadius.circular(18),
1555
- border: Border.all(color: _border),
1556
- ),
1557
- child: Column(
1558
- crossAxisAlignment: CrossAxisAlignment.start,
1559
- children: <Widget>[
1560
- Wrap(
1561
- spacing: 10,
1562
- runSpacing: 10,
1563
- children: <Widget>[
1564
- _StatusPill(
1565
- label: 'Update ${release.version}',
1566
- color: release.channel == 'beta'
1567
- ? _warning
1568
- : _accent,
1569
- ),
1570
- _StatusPill(
1571
- label: release.asset.name,
1572
- color: _textSecondary,
1525
+ },
1526
+ ),
1527
+ const SizedBox(height: 8),
1528
+ Text(
1529
+ 'Installed: ${controller.installedAppVersion ?? 'Unknown'} | Channel: ${controller.appUpdateChannelLabel} | Last checked: ${controller.appUpdateLastCheckedLabel}',
1530
+ style: TextStyle(color: _textSecondary),
1531
+ ),
1532
+ const SizedBox(height: 6),
1533
+ Text(
1534
+ 'Source: ${app_release_updater.appUpdaterGithubOwner}/${app_release_updater.appUpdaterGithubRepo}${app_release_updater.appUpdaterGithubToken.trim().isNotEmpty ? ' (override active)' : ''}',
1535
+ style: TextStyle(color: _textSecondary),
1536
+ ),
1537
+ if (controller.appUpdateErrorMessage
1538
+ case final message?) ...<Widget>[
1539
+ const SizedBox(height: 12),
1540
+ _InlineError(message: message),
1541
+ ],
1542
+ if (controller.availableAppUpdate
1543
+ case final release?) ...<Widget>[
1544
+ const SizedBox(height: 16),
1545
+ Container(
1546
+ width: double.infinity,
1547
+ padding: const EdgeInsets.all(16),
1548
+ decoration: BoxDecoration(
1549
+ color: _bgSecondary,
1550
+ borderRadius: BorderRadius.circular(18),
1551
+ border: Border.all(color: _border),
1552
+ ),
1553
+ child: Column(
1554
+ crossAxisAlignment: CrossAxisAlignment.start,
1555
+ children: <Widget>[
1556
+ Wrap(
1557
+ spacing: 10,
1558
+ runSpacing: 10,
1559
+ children: <Widget>[
1560
+ _StatusPill(
1561
+ label: 'Update ${release.version}',
1562
+ color: release.channel == 'beta'
1563
+ ? _warning
1564
+ : _accent,
1565
+ ),
1566
+ _StatusPill(
1567
+ label: release.asset.name,
1568
+ color: _textSecondary,
1569
+ ),
1570
+ ],
1571
+ ),
1572
+ const SizedBox(height: 10),
1573
+ Text(
1574
+ '${release.title} · ${release.publishedLabel} · ${release.asset.sizeLabel}',
1575
+ style: TextStyle(color: _textSecondary),
1576
+ ),
1577
+ if (release.body.trim().isNotEmpty) ...<Widget>[
1578
+ const SizedBox(height: 14),
1579
+ ConstrainedBox(
1580
+ constraints: const BoxConstraints(maxHeight: 220),
1581
+ child: SingleChildScrollView(
1582
+ child: MarkdownBody(
1583
+ data: release.body,
1584
+ selectable: true,
1585
+ styleSheet: MarkdownStyleSheet(
1586
+ p: TextStyle(
1587
+ color: _textSecondary,
1588
+ height: 1.45,
1589
+ ),
1590
+ ),
1591
+ ),
1592
+ ),
1573
1593
  ),
1574
1594
  ],
1575
- ),
1576
- const SizedBox(height: 10),
1577
- Text(
1578
- '${release.title} · ${release.publishedLabel} · ${release.asset.sizeLabel}',
1579
- style: TextStyle(color: _textSecondary),
1580
- ),
1581
- if (release.body.trim().isNotEmpty) ...<Widget>[
1582
1595
  const SizedBox(height: 14),
1583
- ConstrainedBox(
1584
- constraints: const BoxConstraints(maxHeight: 220),
1585
- child: SingleChildScrollView(
1586
- child: MarkdownBody(
1587
- data: release.body,
1588
- selectable: true,
1589
- styleSheet: MarkdownStyleSheet(
1590
- p: TextStyle(
1591
- color: _textSecondary,
1592
- height: 1.45,
1593
- ),
1596
+ Wrap(
1597
+ spacing: 10,
1598
+ runSpacing: 10,
1599
+ children: <Widget>[
1600
+ FilledButton.icon(
1601
+ onPressed: controller.isOpeningAppUpdate
1602
+ ? null
1603
+ : controller.openAppUpdate,
1604
+ style: FilledButton.styleFrom(
1605
+ backgroundColor: _accent,
1606
+ ),
1607
+ icon: controller.isOpeningAppUpdate
1608
+ ? const SizedBox.square(
1609
+ dimension: 16,
1610
+ child: CircularProgressIndicator(
1611
+ strokeWidth: 2,
1612
+ color: Colors.white,
1613
+ ),
1614
+ )
1615
+ : const Icon(Icons.system_update_alt),
1616
+ label: Text(
1617
+ controller.isOpeningAppUpdate
1618
+ ? 'Opening...'
1619
+ : 'Download update',
1594
1620
  ),
1595
1621
  ),
1596
- ),
1622
+ if (release.htmlUrl.trim().isNotEmpty)
1623
+ OutlinedButton.icon(
1624
+ onPressed: () {
1625
+ unawaited(
1626
+ widget.controller._oauthLauncher
1627
+ .openExternal(
1628
+ url: release.htmlUrl,
1629
+ label: 'release_notes',
1630
+ ),
1631
+ );
1632
+ },
1633
+ icon: const Icon(Icons.open_in_new),
1634
+ label: Text('View release'),
1635
+ ),
1636
+ ],
1597
1637
  ),
1598
1638
  ],
1599
- const SizedBox(height: 14),
1600
- Wrap(
1601
- spacing: 10,
1602
- runSpacing: 10,
1603
- children: <Widget>[
1604
- FilledButton.icon(
1605
- onPressed: controller.isOpeningAppUpdate
1606
- ? null
1607
- : controller.openAppUpdate,
1608
- style: FilledButton.styleFrom(
1609
- backgroundColor: _accent,
1610
- ),
1611
- icon: controller.isOpeningAppUpdate
1612
- ? const SizedBox.square(
1613
- dimension: 16,
1614
- child: CircularProgressIndicator(
1615
- strokeWidth: 2,
1616
- color: Colors.white,
1617
- ),
1618
- )
1619
- : const Icon(Icons.system_update_alt),
1620
- label: Text(
1621
- controller.isOpeningAppUpdate
1622
- ? 'Opening...'
1623
- : 'Download update',
1624
- ),
1625
- ),
1626
- if (release.htmlUrl.trim().isNotEmpty)
1627
- OutlinedButton.icon(
1628
- onPressed: () {
1629
- unawaited(
1630
- widget.controller._oauthLauncher.openExternal(
1631
- url: release.htmlUrl,
1632
- label: 'release_notes',
1633
- ),
1634
- );
1635
- },
1636
- icon: const Icon(Icons.open_in_new),
1637
- label: Text('View release'),
1638
- ),
1639
- ],
1640
- ),
1641
- ],
1639
+ ),
1642
1640
  ),
1643
- ),
1644
- ] else ...<Widget>[
1645
- const SizedBox(height: 12),
1646
- Text(
1647
- controller.isCheckingAppUpdate
1648
- ? 'Checking GitHub releases...'
1649
- : controller.appUpdateLastCheckedAt == null
1650
- ? 'Choose a channel, then check GitHub releases.'
1651
- : 'No newer app release is available on the selected channel.',
1652
- style: TextStyle(color: _textSecondary, height: 1.45),
1653
- ),
1641
+ ] else ...<Widget>[
1642
+ const SizedBox(height: 12),
1643
+ Text(
1644
+ controller.isCheckingAppUpdate
1645
+ ? 'Checking GitHub releases...'
1646
+ : controller.appUpdateLastCheckedAt == null
1647
+ ? 'Choose a channel, then check GitHub releases.'
1648
+ : 'No newer app release is available on the selected channel.',
1649
+ style: TextStyle(color: _textSecondary, height: 1.45),
1650
+ ),
1651
+ ],
1654
1652
  ],
1655
- ],
1656
1653
  const Divider(height: 32),
1657
1654
  if (controller.updateStatus.allowSelfUpdate) ...<Widget>[
1658
1655
  LayoutBuilder(
@@ -1132,6 +1132,13 @@ class _ChatBubble extends StatelessWidget {
1132
1132
  Widget build(BuildContext context) {
1133
1133
  final isUser = entry.role == 'user';
1134
1134
  final isTransient = entry.transient;
1135
+ final sharedAttachments = (entry.metadata['sharedAttachments'] is List)
1136
+ ? (entry.metadata['sharedAttachments'] as List)
1137
+ .whereType<Map>()
1138
+ .map((item) => SharedChatAttachment.fromJson(item))
1139
+ .where((item) => item.isValid)
1140
+ .toList(growable: false)
1141
+ : const <SharedChatAttachment>[];
1135
1142
 
1136
1143
  if (entry.typing) {
1137
1144
  return const _TypingIndicatorBubble();
@@ -1206,6 +1213,77 @@ class _ChatBubble extends StatelessWidget {
1206
1213
  ),
1207
1214
  ),
1208
1215
  ),
1216
+ if (sharedAttachments.isNotEmpty) ...<Widget>[
1217
+ const SizedBox(height: 10),
1218
+ Wrap(
1219
+ spacing: 8,
1220
+ runSpacing: 8,
1221
+ children: sharedAttachments
1222
+ .map((attachment) {
1223
+ final icon =
1224
+ attachment.mimeType.toLowerCase().startsWith(
1225
+ 'video/',
1226
+ )
1227
+ ? Icons.videocam_outlined
1228
+ : attachment.mimeType.toLowerCase().startsWith(
1229
+ 'image/',
1230
+ )
1231
+ ? Icons.image_outlined
1232
+ : attachment.mimeType.toLowerCase().startsWith(
1233
+ 'audio/',
1234
+ )
1235
+ ? Icons.audiotrack_outlined
1236
+ : Icons.attach_file_rounded;
1237
+ return Container(
1238
+ padding: const EdgeInsets.symmetric(
1239
+ horizontal: 10,
1240
+ vertical: 7,
1241
+ ),
1242
+ decoration: BoxDecoration(
1243
+ color: isUser
1244
+ ? const Color(0x1FFFFFFF)
1245
+ : _bgSecondary,
1246
+ borderRadius: BorderRadius.circular(999),
1247
+ border: Border.all(
1248
+ color: isUser
1249
+ ? const Color(0x40FFFFFF)
1250
+ : _border,
1251
+ ),
1252
+ ),
1253
+ child: Row(
1254
+ mainAxisSize: MainAxisSize.min,
1255
+ children: <Widget>[
1256
+ Icon(
1257
+ icon,
1258
+ size: 14,
1259
+ color: isUser
1260
+ ? Colors.white
1261
+ : _textSecondary,
1262
+ ),
1263
+ const SizedBox(width: 6),
1264
+ ConstrainedBox(
1265
+ constraints: const BoxConstraints(
1266
+ maxWidth: 180,
1267
+ ),
1268
+ child: Text(
1269
+ attachment.name,
1270
+ maxLines: 1,
1271
+ overflow: TextOverflow.ellipsis,
1272
+ style: TextStyle(
1273
+ color: isUser
1274
+ ? Colors.white
1275
+ : _textPrimary,
1276
+ fontSize: 12,
1277
+ ),
1278
+ ),
1279
+ ),
1280
+ ],
1281
+ ),
1282
+ );
1283
+ })
1284
+ .toList(growable: false),
1285
+ ),
1286
+ ],
1209
1287
  if (!isUser &&
1210
1288
  entry.runId?.trim().isNotEmpty == true) ...<Widget>[
1211
1289
  const SizedBox(height: 12),
@@ -1,27 +1,56 @@
1
1
  import 'package:flutter/foundation.dart';
2
2
  import 'package:flutter/services.dart';
3
3
 
4
+ class AppLaunchRequest {
5
+ const AppLaunchRequest({
6
+ required this.action,
7
+ this.text,
8
+ this.subject,
9
+ this.files = const <Map<String, dynamic>>[],
10
+ });
11
+
12
+ factory AppLaunchRequest.fromEvent(dynamic event) {
13
+ if (event is Map) {
14
+ final map = Map<String, dynamic>.from(event);
15
+ final files = (map['files'] is List)
16
+ ? (map['files'] as List)
17
+ .whereType<Map>()
18
+ .map((item) => Map<String, dynamic>.from(item))
19
+ .toList()
20
+ : const <Map<String, dynamic>>[];
21
+ return AppLaunchRequest(
22
+ action: map['action']?.toString() ?? '',
23
+ text: map['text']?.toString(),
24
+ subject: map['subject']?.toString(),
25
+ files: files,
26
+ );
27
+ }
28
+ return AppLaunchRequest(action: event?.toString() ?? '');
29
+ }
30
+
31
+ final String action;
32
+ final String? text;
33
+ final String? subject;
34
+ final List<Map<String, dynamic>> files;
35
+ }
36
+
4
37
  class AppLaunchBridge {
5
38
  static const String voiceAssistantAction = 'voice_assistant';
39
+ static const String shareToChatAction = 'share_to_chat';
6
40
  static const EventChannel _events = EventChannel(
7
41
  'neoagent/app_launch/events',
8
42
  );
9
43
 
10
- static Stream<String>? _launchRequests;
44
+ static Stream<AppLaunchRequest>? _launchRequests;
11
45
 
12
- Stream<String> get launchRequests {
46
+ Stream<AppLaunchRequest> get launchRequests {
13
47
  if (!_isAndroid) {
14
- return const Stream<String>.empty();
48
+ return const Stream<AppLaunchRequest>.empty();
15
49
  }
16
50
  return _launchRequests ??= _events
17
51
  .receiveBroadcastStream()
18
- .map((dynamic event) {
19
- if (event is Map) {
20
- return event['action']?.toString() ?? '';
21
- }
22
- return event?.toString() ?? '';
23
- })
24
- .where((action) => action.trim().isNotEmpty);
52
+ .map(AppLaunchRequest.fromEvent)
53
+ .where((request) => request.action.trim().isNotEmpty);
25
54
  }
26
55
 
27
56
  bool get _isAndroid =>
@@ -1243,6 +1243,34 @@ class BackendClient {
1243
1243
  return getMap(baseUrl, _withAgentQuery('/api/memory', agentId));
1244
1244
  }
1245
1245
 
1246
+ Future<Map<String, dynamic>> fetchMemoryTransferPrompt(
1247
+ String baseUrl, {
1248
+ String? agentId,
1249
+ }) async {
1250
+ return getMap(
1251
+ baseUrl,
1252
+ _withAgentQuery('/api/memory/transfer-prompt', agentId),
1253
+ );
1254
+ }
1255
+
1256
+ Future<Map<String, dynamic>> importMemoryTransfer(
1257
+ String baseUrl, {
1258
+ required String text,
1259
+ bool applyBehaviorNotes = true,
1260
+ bool applyCoreMemory = true,
1261
+ String? agentId,
1262
+ }) async {
1263
+ return postMap(
1264
+ baseUrl,
1265
+ '/api/memory/transfer-import',
1266
+ _withAgentId(<String, dynamic>{
1267
+ 'text': text,
1268
+ 'applyBehaviorNotes': applyBehaviorNotes,
1269
+ 'applyCoreMemory': applyCoreMemory,
1270
+ }, agentId),
1271
+ );
1272
+ }
1273
+
1246
1274
  Future<List<Map<String, dynamic>>> fetchMemories(
1247
1275
  String baseUrl, {
1248
1276
  String? category,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.3.1-beta.85",
3
+ "version": "2.3.1-beta.87",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "neoagent-guest-agent-android",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "description": "Guest runtime for NeoAgent Android and CLI services",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "dependencies": {
10
+ "express": "^4.21.2",
11
+ "proper-lockfile": "^4.1.2"
12
+ }
13
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "neoagent-guest-agent-browser",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "description": "Guest runtime for NeoAgent browser and CLI services",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "dependencies": {
10
+ "express": "^4.21.2",
11
+ "playwright-chromium": "^1.59.1",
12
+ "proper-lockfile": "^4.1.2"
13
+ }
14
+ }