neoagent 3.1.0 → 3.1.1-beta.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/flutter_app/lib/main_integrations.dart +252 -0
- package/flutter_app/lib/main_operations.dart +364 -322
- package/package.json +2 -1
- package/server/guest-agent.browser.package.json +1 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +38078 -37802
- package/server/services/integrations/neomail/provider.js +993 -0
- package/server/services/integrations/registry.js +2 -0
- package/server/services/tasks/adapters/index.js +1 -1
- package/server/services/tasks/adapters/neomail_email_received.js +46 -0
- package/server/services/tasks/integration_runtime.js +67 -3
- package/server/services/tasks/runtime.js +6 -1
- package/server/services/tasks/task_repository.js +13 -6
|
@@ -505,8 +505,10 @@ class _SkillsPanelState extends State<SkillsPanel>
|
|
|
505
505
|
|
|
506
506
|
// Installed tab search & filter state
|
|
507
507
|
String _installedQuery = '';
|
|
508
|
-
String _installedStatusFilter =
|
|
509
|
-
|
|
508
|
+
String _installedStatusFilter =
|
|
509
|
+
'all'; // 'all' | 'active' | 'draft' | 'disabled'
|
|
510
|
+
String _installedSourceFilter =
|
|
511
|
+
'all'; // 'all' | 'built-in' | 'learned' | 'user' | 'store'
|
|
510
512
|
late final TextEditingController _installedSearchController;
|
|
511
513
|
|
|
512
514
|
@override
|
|
@@ -645,18 +647,36 @@ class _SkillsPanelState extends State<SkillsPanel>
|
|
|
645
647
|
final q = _installedQuery;
|
|
646
648
|
if (q.isNotEmpty &&
|
|
647
649
|
!skill.name.toLowerCase().contains(q) &&
|
|
648
|
-
!skill.description.toLowerCase().contains(q))
|
|
650
|
+
!skill.description.toLowerCase().contains(q)) {
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
649
653
|
if (_installedStatusFilter != 'all') {
|
|
650
|
-
if (_installedStatusFilter == 'active' &&
|
|
651
|
-
|
|
652
|
-
|
|
654
|
+
if (_installedStatusFilter == 'active' &&
|
|
655
|
+
(!skill.enabled || skill.draft)) {
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
if (_installedStatusFilter == 'draft' && !skill.draft) {
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
if (_installedStatusFilter == 'disabled' && skill.enabled) {
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (_installedSourceFilter != 'all' &&
|
|
666
|
+
skill.source != _installedSourceFilter) {
|
|
667
|
+
return false;
|
|
653
668
|
}
|
|
654
|
-
if (_installedSourceFilter != 'all' && skill.source != _installedSourceFilter) return false;
|
|
655
669
|
return true;
|
|
656
670
|
}).toList();
|
|
657
671
|
|
|
658
672
|
final statusFilters = <String>['all', 'active', 'draft', 'disabled'];
|
|
659
|
-
final sourceFilters = <String>[
|
|
673
|
+
final sourceFilters = <String>[
|
|
674
|
+
'all',
|
|
675
|
+
'built-in',
|
|
676
|
+
'learned',
|
|
677
|
+
'user',
|
|
678
|
+
'store',
|
|
679
|
+
];
|
|
660
680
|
|
|
661
681
|
return Card(
|
|
662
682
|
child: Column(
|
|
@@ -757,157 +777,174 @@ class _SkillsPanelState extends State<SkillsPanel>
|
|
|
757
777
|
separatorBuilder: (_, __) => const SizedBox(height: 10),
|
|
758
778
|
itemBuilder: (context, index) {
|
|
759
779
|
final skill = filteredSkills[index];
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
780
|
+
return LayoutBuilder(
|
|
781
|
+
builder: (context, constraints) {
|
|
782
|
+
final compact = constraints.maxWidth < 760;
|
|
783
|
+
return Container(
|
|
784
|
+
padding: const EdgeInsets.all(14),
|
|
785
|
+
decoration: BoxDecoration(
|
|
786
|
+
color: _bgSecondary,
|
|
787
|
+
borderRadius: BorderRadius.circular(14),
|
|
788
|
+
border: Border.all(color: _border),
|
|
789
|
+
),
|
|
790
|
+
child: compact
|
|
791
|
+
? Column(
|
|
792
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
793
|
+
children: <Widget>[
|
|
794
|
+
Row(
|
|
795
|
+
children: <Widget>[
|
|
796
|
+
Expanded(
|
|
797
|
+
child: Text(
|
|
798
|
+
skill.name,
|
|
799
|
+
style: TextStyle(
|
|
800
|
+
fontWeight: FontWeight.w700,
|
|
801
|
+
),
|
|
802
|
+
),
|
|
803
|
+
),
|
|
804
|
+
Switch(
|
|
805
|
+
value: skill.enabled,
|
|
806
|
+
onChanged: (value) => controller
|
|
807
|
+
.setSkillEnabled(skill.name, value),
|
|
808
|
+
),
|
|
809
|
+
],
|
|
810
|
+
),
|
|
811
|
+
Text(
|
|
812
|
+
skill.description.ifEmpty('No description'),
|
|
813
|
+
style: TextStyle(color: _textSecondary),
|
|
814
|
+
),
|
|
815
|
+
const SizedBox(height: 10),
|
|
816
|
+
Wrap(
|
|
817
|
+
spacing: 8,
|
|
818
|
+
runSpacing: 8,
|
|
819
|
+
children: <Widget>[
|
|
820
|
+
_MetaPill(
|
|
821
|
+
label: skill.category,
|
|
822
|
+
icon: Icons.folder_outlined,
|
|
823
|
+
),
|
|
824
|
+
_MetaPill(
|
|
825
|
+
label: skill.source,
|
|
826
|
+
icon: Icons.source_outlined,
|
|
827
|
+
),
|
|
828
|
+
if (skill.draft)
|
|
829
|
+
const _MetaPill(
|
|
830
|
+
label: 'Draft',
|
|
831
|
+
icon: Icons.edit_note_outlined,
|
|
832
|
+
),
|
|
833
|
+
],
|
|
834
|
+
),
|
|
835
|
+
const SizedBox(height: 10),
|
|
836
|
+
Row(
|
|
837
|
+
children: <Widget>[
|
|
838
|
+
const Spacer(),
|
|
839
|
+
OutlinedButton(
|
|
840
|
+
onPressed: () => _openSkillEditor(
|
|
841
|
+
context,
|
|
842
|
+
skill.name,
|
|
843
|
+
),
|
|
844
|
+
child: Text('Open'),
|
|
845
|
+
),
|
|
846
|
+
const SizedBox(width: 8),
|
|
847
|
+
TextButton.icon(
|
|
848
|
+
onPressed: () => _confirmDeleteSkill(
|
|
849
|
+
context,
|
|
850
|
+
skill.name,
|
|
851
|
+
),
|
|
852
|
+
icon: Icon(Icons.delete_outline),
|
|
853
|
+
style: TextButton.styleFrom(
|
|
854
|
+
foregroundColor: _danger,
|
|
855
|
+
),
|
|
856
|
+
label: Text('Delete'),
|
|
857
|
+
),
|
|
858
|
+
],
|
|
859
|
+
),
|
|
860
|
+
],
|
|
861
|
+
)
|
|
862
|
+
: Row(
|
|
863
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
864
|
+
children: <Widget>[
|
|
865
|
+
Expanded(
|
|
866
|
+
child: Column(
|
|
867
|
+
crossAxisAlignment:
|
|
868
|
+
CrossAxisAlignment.start,
|
|
869
|
+
children: <Widget>[
|
|
870
|
+
Text(
|
|
871
|
+
skill.name,
|
|
872
|
+
style: TextStyle(
|
|
873
|
+
fontWeight: FontWeight.w700,
|
|
874
|
+
),
|
|
875
|
+
),
|
|
876
|
+
const SizedBox(height: 6),
|
|
877
|
+
Text(
|
|
878
|
+
skill.description.ifEmpty(
|
|
879
|
+
'No description',
|
|
880
|
+
),
|
|
881
|
+
style: TextStyle(
|
|
882
|
+
color: _textSecondary,
|
|
883
|
+
),
|
|
884
|
+
),
|
|
885
|
+
const SizedBox(height: 10),
|
|
886
|
+
Wrap(
|
|
887
|
+
spacing: 8,
|
|
888
|
+
runSpacing: 8,
|
|
889
|
+
children: <Widget>[
|
|
890
|
+
_MetaPill(
|
|
891
|
+
label: skill.category,
|
|
892
|
+
icon: Icons.folder_outlined,
|
|
893
|
+
),
|
|
894
|
+
_MetaPill(
|
|
895
|
+
label: skill.source,
|
|
896
|
+
icon: Icons.source_outlined,
|
|
897
|
+
),
|
|
898
|
+
if (skill.draft)
|
|
899
|
+
const _MetaPill(
|
|
900
|
+
label: 'Draft',
|
|
901
|
+
icon: Icons.edit_note_outlined,
|
|
902
|
+
),
|
|
903
|
+
],
|
|
904
|
+
),
|
|
905
|
+
],
|
|
864
906
|
),
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
907
|
+
),
|
|
908
|
+
const SizedBox(width: 10),
|
|
909
|
+
Column(
|
|
910
|
+
children: <Widget>[
|
|
911
|
+
Switch(
|
|
912
|
+
value: skill.enabled,
|
|
913
|
+
onChanged: (value) => controller
|
|
914
|
+
.setSkillEnabled(skill.name, value),
|
|
869
915
|
),
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
icon: Icon(Icons.delete_outline),
|
|
893
|
-
style: TextButton.styleFrom(
|
|
894
|
-
foregroundColor: _danger,
|
|
895
|
-
),
|
|
896
|
-
label: Text('Delete'),
|
|
916
|
+
OutlinedButton(
|
|
917
|
+
onPressed: () => _openSkillEditor(
|
|
918
|
+
context,
|
|
919
|
+
skill.name,
|
|
920
|
+
),
|
|
921
|
+
child: Text('Open'),
|
|
922
|
+
),
|
|
923
|
+
const SizedBox(height: 6),
|
|
924
|
+
TextButton.icon(
|
|
925
|
+
onPressed: () => _confirmDeleteSkill(
|
|
926
|
+
context,
|
|
927
|
+
skill.name,
|
|
928
|
+
),
|
|
929
|
+
icon: Icon(Icons.delete_outline),
|
|
930
|
+
style: TextButton.styleFrom(
|
|
931
|
+
foregroundColor: _danger,
|
|
932
|
+
),
|
|
933
|
+
label: Text('Delete'),
|
|
934
|
+
),
|
|
935
|
+
],
|
|
936
|
+
),
|
|
937
|
+
],
|
|
897
938
|
),
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
),
|
|
902
|
-
);
|
|
903
|
-
},
|
|
904
|
-
);
|
|
939
|
+
);
|
|
940
|
+
},
|
|
941
|
+
);
|
|
905
942
|
},
|
|
906
943
|
),
|
|
907
944
|
),
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
945
|
+
],
|
|
946
|
+
),
|
|
947
|
+
);
|
|
911
948
|
}
|
|
912
949
|
|
|
913
950
|
Widget _buildStoreTab(
|
|
@@ -1465,8 +1502,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1465
1502
|
);
|
|
1466
1503
|
if (!mounted) return;
|
|
1467
1504
|
_llmImportController.clear();
|
|
1468
|
-
final warningText =
|
|
1469
|
-
|
|
1505
|
+
final warningText = result.warnings.isEmpty
|
|
1506
|
+
? ''
|
|
1507
|
+
: ' ${result.warnings.join(' ')}';
|
|
1470
1508
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
1471
1509
|
SnackBar(
|
|
1472
1510
|
content: Text(
|
|
@@ -1494,9 +1532,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1494
1532
|
: controller.memories;
|
|
1495
1533
|
if (_entityFilter == null) return base;
|
|
1496
1534
|
return base
|
|
1497
|
-
.where(
|
|
1498
|
-
(m) => m.entities.any((e) => e.name == _entityFilter),
|
|
1499
|
-
)
|
|
1535
|
+
.where((m) => m.entities.any((e) => e.name == _entityFilter))
|
|
1500
1536
|
.toList();
|
|
1501
1537
|
}
|
|
1502
1538
|
|
|
@@ -1588,7 +1624,10 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1588
1624
|
});
|
|
1589
1625
|
}
|
|
1590
1626
|
|
|
1591
|
-
void _openRetrievalInspector(
|
|
1627
|
+
void _openRetrievalInspector(
|
|
1628
|
+
BuildContext context,
|
|
1629
|
+
NeoAgentController controller,
|
|
1630
|
+
) {
|
|
1592
1631
|
Navigator.of(context).push(
|
|
1593
1632
|
MaterialPageRoute(
|
|
1594
1633
|
builder: (context) => RetrievalInspectorView(controller: controller),
|
|
@@ -1603,7 +1642,8 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1603
1642
|
final memoriesToShow = _visibleMemories;
|
|
1604
1643
|
final selectedIds = _selectedVisibleMemoryIds.toSet();
|
|
1605
1644
|
final selectedCount = selectedIds.length;
|
|
1606
|
-
final allVisibleSelected =
|
|
1645
|
+
final allVisibleSelected =
|
|
1646
|
+
memoriesToShow.isNotEmpty &&
|
|
1607
1647
|
memoriesToShow.every((m) => selectedIds.contains(m.id));
|
|
1608
1648
|
final showingSearchResults = controller.memoryRecallResults.isNotEmpty;
|
|
1609
1649
|
final compact = MediaQuery.sizeOf(context).width < 760;
|
|
@@ -1644,9 +1684,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1644
1684
|
padding: const EdgeInsets.all(18),
|
|
1645
1685
|
child: Row(
|
|
1646
1686
|
children: <Widget>[
|
|
1647
|
-
_MemoryConfidenceGauge(
|
|
1648
|
-
confidence: stats.averageConfidence,
|
|
1649
|
-
),
|
|
1687
|
+
_MemoryConfidenceGauge(confidence: stats.averageConfidence),
|
|
1650
1688
|
const SizedBox(width: 18),
|
|
1651
1689
|
Expanded(
|
|
1652
1690
|
child: Wrap(
|
|
@@ -1705,9 +1743,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1705
1743
|
children: <Widget>[
|
|
1706
1744
|
Row(
|
|
1707
1745
|
children: <Widget>[
|
|
1708
|
-
Expanded(
|
|
1709
|
-
child: const _SectionTitle('Knowledge Graph'),
|
|
1710
|
-
),
|
|
1746
|
+
Expanded(child: const _SectionTitle('Knowledge Graph')),
|
|
1711
1747
|
if (_entityFilter != null)
|
|
1712
1748
|
TextButton.icon(
|
|
1713
1749
|
onPressed: () =>
|
|
@@ -1720,10 +1756,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1720
1756
|
const SizedBox(height: 4),
|
|
1721
1757
|
Text(
|
|
1722
1758
|
'Tap an entity to filter memories by it.',
|
|
1723
|
-
style: TextStyle(
|
|
1724
|
-
color: _textSecondary,
|
|
1725
|
-
fontSize: 12,
|
|
1726
|
-
),
|
|
1759
|
+
style: TextStyle(color: _textSecondary, fontSize: 12),
|
|
1727
1760
|
),
|
|
1728
1761
|
const SizedBox(height: 14),
|
|
1729
1762
|
SizedBox(
|
|
@@ -1857,15 +1890,16 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1857
1890
|
Wrap(
|
|
1858
1891
|
spacing: 8,
|
|
1859
1892
|
runSpacing: 8,
|
|
1860
|
-
crossAxisAlignment:
|
|
1861
|
-
WrapCrossAlignment.center,
|
|
1893
|
+
crossAxisAlignment: WrapCrossAlignment.center,
|
|
1862
1894
|
children: <Widget>[
|
|
1863
1895
|
OutlinedButton.icon(
|
|
1864
|
-
onPressed:
|
|
1896
|
+
onPressed:
|
|
1897
|
+
allVisibleSelected ||
|
|
1865
1898
|
_bulkActionInFlight
|
|
1866
1899
|
? null
|
|
1867
1900
|
: () => _selectAllVisibleMemories(
|
|
1868
|
-
memoriesToShow
|
|
1901
|
+
memoriesToShow,
|
|
1902
|
+
),
|
|
1869
1903
|
icon: Icon(
|
|
1870
1904
|
Icons.done_all_outlined,
|
|
1871
1905
|
size: 16,
|
|
@@ -1903,9 +1937,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1903
1937
|
Icons.archive_outlined,
|
|
1904
1938
|
size: 16,
|
|
1905
1939
|
),
|
|
1906
|
-
label: Text(
|
|
1907
|
-
'Archive ($selectedCount)',
|
|
1908
|
-
),
|
|
1940
|
+
label: Text('Archive ($selectedCount)'),
|
|
1909
1941
|
),
|
|
1910
1942
|
OutlinedButton.icon(
|
|
1911
1943
|
onPressed: _bulkActionInFlight
|
|
@@ -1923,9 +1955,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1923
1955
|
Icons.delete_sweep_outlined,
|
|
1924
1956
|
size: 16,
|
|
1925
1957
|
),
|
|
1926
|
-
label: Text(
|
|
1927
|
-
'Delete ($selectedCount)',
|
|
1928
|
-
),
|
|
1958
|
+
label: Text('Delete ($selectedCount)'),
|
|
1929
1959
|
),
|
|
1930
1960
|
],
|
|
1931
1961
|
],
|
|
@@ -1941,8 +1971,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1941
1971
|
)
|
|
1942
1972
|
else
|
|
1943
1973
|
...memoriesToShow.map((memory) {
|
|
1944
|
-
final isSelected =
|
|
1945
|
-
|
|
1974
|
+
final isSelected = selectedIds.contains(
|
|
1975
|
+
memory.id,
|
|
1976
|
+
);
|
|
1946
1977
|
return _MemoryRow(
|
|
1947
1978
|
memory: memory,
|
|
1948
1979
|
isSelected: isSelected,
|
|
@@ -1950,8 +1981,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1950
1981
|
memory.id,
|
|
1951
1982
|
!isSelected,
|
|
1952
1983
|
),
|
|
1953
|
-
onCheck: (value) =>
|
|
1954
|
-
_toggleMemorySelection(
|
|
1984
|
+
onCheck: (value) => _toggleMemorySelection(
|
|
1955
1985
|
memory.id,
|
|
1956
1986
|
value ?? false,
|
|
1957
1987
|
),
|
|
@@ -1964,9 +1994,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1964
1994
|
'This memory will be removed permanently.',
|
|
1965
1995
|
onConfirm: () =>
|
|
1966
1996
|
_deleteSingleMemory(
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1997
|
+
controller,
|
|
1998
|
+
memory.id,
|
|
1999
|
+
),
|
|
1970
2000
|
),
|
|
1971
2001
|
);
|
|
1972
2002
|
}),
|
|
@@ -1985,9 +2015,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
1985
2015
|
Expanded(
|
|
1986
2016
|
child: Text(
|
|
1987
2017
|
'Key-value pairs that persist across conversations.',
|
|
1988
|
-
style: TextStyle(
|
|
1989
|
-
color: _textSecondary,
|
|
1990
|
-
),
|
|
2018
|
+
style: TextStyle(color: _textSecondary),
|
|
1991
2019
|
),
|
|
1992
2020
|
),
|
|
1993
2021
|
TextButton.icon(
|
|
@@ -2001,75 +2029,75 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2001
2029
|
],
|
|
2002
2030
|
),
|
|
2003
2031
|
const SizedBox(height: 10),
|
|
2004
|
-
if (controller
|
|
2005
|
-
.memoryOverview.coreEntries.isEmpty)
|
|
2032
|
+
if (controller.memoryOverview.coreEntries.isEmpty)
|
|
2006
2033
|
Text(
|
|
2007
2034
|
'No core memory entries yet.',
|
|
2008
2035
|
style: TextStyle(color: _textSecondary),
|
|
2009
2036
|
)
|
|
2010
2037
|
else
|
|
2011
|
-
...controller.memoryOverview.coreEntries
|
|
2012
|
-
.entries
|
|
2038
|
+
...controller.memoryOverview.coreEntries.entries
|
|
2013
2039
|
.map((entry) {
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
decoration: BoxDecoration(
|
|
2019
|
-
color: _bgSecondary,
|
|
2020
|
-
borderRadius:
|
|
2021
|
-
BorderRadius.circular(12),
|
|
2022
|
-
border: Border.all(color: _border),
|
|
2023
|
-
),
|
|
2024
|
-
child: Row(
|
|
2025
|
-
crossAxisAlignment:
|
|
2026
|
-
CrossAxisAlignment.start,
|
|
2027
|
-
children: <Widget>[
|
|
2028
|
-
Expanded(
|
|
2029
|
-
child: Column(
|
|
2030
|
-
crossAxisAlignment:
|
|
2031
|
-
CrossAxisAlignment.start,
|
|
2032
|
-
children: <Widget>[
|
|
2033
|
-
Text(
|
|
2034
|
-
entry.key,
|
|
2035
|
-
style: TextStyle(
|
|
2036
|
-
fontWeight: FontWeight.w700,
|
|
2037
|
-
),
|
|
2038
|
-
),
|
|
2039
|
-
const SizedBox(height: 6),
|
|
2040
|
-
Text(
|
|
2041
|
-
entry.value.toString(),
|
|
2042
|
-
),
|
|
2043
|
-
],
|
|
2044
|
-
),
|
|
2040
|
+
return Container(
|
|
2041
|
+
width: double.infinity,
|
|
2042
|
+
margin: const EdgeInsets.only(
|
|
2043
|
+
bottom: 10,
|
|
2045
2044
|
),
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
keyValue: entry,
|
|
2045
|
+
padding: const EdgeInsets.all(12),
|
|
2046
|
+
decoration: BoxDecoration(
|
|
2047
|
+
color: _bgSecondary,
|
|
2048
|
+
borderRadius: BorderRadius.circular(
|
|
2049
|
+
12,
|
|
2052
2050
|
),
|
|
2053
|
-
|
|
2051
|
+
border: Border.all(color: _border),
|
|
2054
2052
|
),
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2053
|
+
child: Row(
|
|
2054
|
+
crossAxisAlignment:
|
|
2055
|
+
CrossAxisAlignment.start,
|
|
2056
|
+
children: <Widget>[
|
|
2057
|
+
Expanded(
|
|
2058
|
+
child: Column(
|
|
2059
|
+
crossAxisAlignment:
|
|
2060
|
+
CrossAxisAlignment.start,
|
|
2061
|
+
children: <Widget>[
|
|
2062
|
+
Text(
|
|
2063
|
+
entry.key,
|
|
2064
|
+
style: TextStyle(
|
|
2065
|
+
fontWeight:
|
|
2066
|
+
FontWeight.w700,
|
|
2067
|
+
),
|
|
2068
|
+
),
|
|
2069
|
+
const SizedBox(height: 6),
|
|
2070
|
+
Text(entry.value.toString()),
|
|
2071
|
+
],
|
|
2072
|
+
),
|
|
2065
2073
|
),
|
|
2066
|
-
|
|
2067
|
-
|
|
2074
|
+
IconButton(
|
|
2075
|
+
onPressed: () =>
|
|
2076
|
+
_openCoreMemoryEditor(
|
|
2077
|
+
context,
|
|
2078
|
+
controller,
|
|
2079
|
+
keyValue: entry,
|
|
2080
|
+
),
|
|
2081
|
+
icon: Icon(Icons.edit_outlined),
|
|
2082
|
+
),
|
|
2083
|
+
IconButton(
|
|
2084
|
+
onPressed: () => _confirmDelete(
|
|
2085
|
+
context,
|
|
2086
|
+
title:
|
|
2087
|
+
'Delete core memory entry?',
|
|
2088
|
+
message:
|
|
2089
|
+
'Remove "${entry.key}" from core memory.',
|
|
2090
|
+
onConfirm: () =>
|
|
2091
|
+
controller.deleteCoreMemory(
|
|
2092
|
+
entry.key,
|
|
2093
|
+
),
|
|
2094
|
+
),
|
|
2095
|
+
icon: Icon(Icons.delete_outline),
|
|
2096
|
+
),
|
|
2097
|
+
],
|
|
2068
2098
|
),
|
|
2069
|
-
|
|
2070
|
-
),
|
|
2071
|
-
);
|
|
2072
|
-
}),
|
|
2099
|
+
);
|
|
2100
|
+
}),
|
|
2073
2101
|
],
|
|
2074
2102
|
),
|
|
2075
2103
|
),
|
|
@@ -2092,11 +2120,8 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2092
2120
|
FilledButton.icon(
|
|
2093
2121
|
onPressed: _llmPromptLoading
|
|
2094
2122
|
? null
|
|
2095
|
-
: () =>
|
|
2096
|
-
|
|
2097
|
-
icon: Icon(
|
|
2098
|
-
Icons.auto_awesome_outlined,
|
|
2099
|
-
),
|
|
2123
|
+
: () => _loadLlmPrompt(controller),
|
|
2124
|
+
icon: Icon(Icons.auto_awesome_outlined),
|
|
2100
2125
|
label: Text(
|
|
2101
2126
|
_llmPromptLoading
|
|
2102
2127
|
? 'Generating...'
|
|
@@ -2105,11 +2130,9 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2105
2130
|
),
|
|
2106
2131
|
OutlinedButton.icon(
|
|
2107
2132
|
onPressed:
|
|
2108
|
-
_llmPromptController.text
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
? null
|
|
2112
|
-
: _copyLlmPrompt,
|
|
2133
|
+
_llmPromptController.text.trim().isEmpty
|
|
2134
|
+
? null
|
|
2135
|
+
: _copyLlmPrompt,
|
|
2113
2136
|
icon: Icon(Icons.copy_all_outlined),
|
|
2114
2137
|
label: Text('Copy Prompt'),
|
|
2115
2138
|
),
|
|
@@ -2122,8 +2145,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2122
2145
|
maxLines: 8,
|
|
2123
2146
|
readOnly: true,
|
|
2124
2147
|
decoration: const InputDecoration(
|
|
2125
|
-
labelText:
|
|
2126
|
-
'Prompt to paste into another AI',
|
|
2148
|
+
labelText: 'Prompt to paste into another AI',
|
|
2127
2149
|
),
|
|
2128
2150
|
),
|
|
2129
2151
|
const SizedBox(height: 16),
|
|
@@ -2133,8 +2155,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2133
2155
|
onChanged: _llmImporting
|
|
2134
2156
|
? null
|
|
2135
2157
|
: (value) => setState(
|
|
2136
|
-
() =>
|
|
2137
|
-
_llmApplyBehaviorNotes = value,
|
|
2158
|
+
() => _llmApplyBehaviorNotes = value,
|
|
2138
2159
|
),
|
|
2139
2160
|
title: Text('Apply behavior notes'),
|
|
2140
2161
|
subtitle: Text(
|
|
@@ -2147,8 +2168,7 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2147
2168
|
onChanged: _llmImporting
|
|
2148
2169
|
? null
|
|
2149
2170
|
: (value) => setState(
|
|
2150
|
-
() =>
|
|
2151
|
-
_llmApplyCoreMemory = value,
|
|
2171
|
+
() => _llmApplyCoreMemory = value,
|
|
2152
2172
|
),
|
|
2153
2173
|
title: Text('Apply core memory'),
|
|
2154
2174
|
subtitle: Text(
|
|
@@ -2168,13 +2188,10 @@ class _MemoryPanelState extends State<MemoryPanel>
|
|
|
2168
2188
|
FilledButton.icon(
|
|
2169
2189
|
onPressed: _llmImporting
|
|
2170
2190
|
? null
|
|
2171
|
-
: () =>
|
|
2172
|
-
_importLlmMemories(controller),
|
|
2191
|
+
: () => _importLlmMemories(controller),
|
|
2173
2192
|
icon: Icon(Icons.file_download_outlined),
|
|
2174
2193
|
label: Text(
|
|
2175
|
-
_llmImporting
|
|
2176
|
-
? 'Importing...'
|
|
2177
|
-
: 'Import',
|
|
2194
|
+
_llmImporting ? 'Importing...' : 'Import',
|
|
2178
2195
|
),
|
|
2179
2196
|
),
|
|
2180
2197
|
],
|
|
@@ -2453,9 +2470,10 @@ class _MemoryConfidenceGaugeState extends State<_MemoryConfidenceGauge>
|
|
|
2453
2470
|
vsync: this,
|
|
2454
2471
|
duration: const Duration(milliseconds: 1200),
|
|
2455
2472
|
);
|
|
2456
|
-
_progress = Tween<double>(
|
|
2457
|
-
|
|
2458
|
-
|
|
2473
|
+
_progress = Tween<double>(
|
|
2474
|
+
begin: 0,
|
|
2475
|
+
end: widget.confidence,
|
|
2476
|
+
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic));
|
|
2459
2477
|
_controller.forward();
|
|
2460
2478
|
}
|
|
2461
2479
|
|
|
@@ -2463,12 +2481,10 @@ class _MemoryConfidenceGaugeState extends State<_MemoryConfidenceGauge>
|
|
|
2463
2481
|
void didUpdateWidget(_MemoryConfidenceGauge oldWidget) {
|
|
2464
2482
|
super.didUpdateWidget(oldWidget);
|
|
2465
2483
|
if (oldWidget.confidence != widget.confidence) {
|
|
2466
|
-
_progress = Tween<double>(
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic),
|
|
2471
|
-
);
|
|
2484
|
+
_progress = Tween<double>(begin: _progress.value, end: widget.confidence)
|
|
2485
|
+
.animate(
|
|
2486
|
+
CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic),
|
|
2487
|
+
);
|
|
2472
2488
|
_controller
|
|
2473
2489
|
..reset()
|
|
2474
2490
|
..forward();
|
|
@@ -2658,10 +2674,7 @@ class _MemoryRow extends StatelessWidget {
|
|
|
2658
2674
|
if (onDelete != null)
|
|
2659
2675
|
IconButton(
|
|
2660
2676
|
onPressed: onDelete,
|
|
2661
|
-
icon: Icon(
|
|
2662
|
-
Icons.delete_outline,
|
|
2663
|
-
size: 18,
|
|
2664
|
-
),
|
|
2677
|
+
icon: Icon(Icons.delete_outline, size: 18),
|
|
2665
2678
|
),
|
|
2666
2679
|
],
|
|
2667
2680
|
),
|
|
@@ -2686,10 +2699,7 @@ class _MemoryRow extends StatelessWidget {
|
|
|
2686
2699
|
const SizedBox(height: 6),
|
|
2687
2700
|
Text(
|
|
2688
2701
|
memory.createdAtLabel,
|
|
2689
|
-
style: TextStyle(
|
|
2690
|
-
fontSize: 11,
|
|
2691
|
-
color: _textMuted,
|
|
2692
|
-
),
|
|
2702
|
+
style: TextStyle(fontSize: 11, color: _textMuted),
|
|
2693
2703
|
),
|
|
2694
2704
|
],
|
|
2695
2705
|
),
|
|
@@ -2778,27 +2788,31 @@ class _EntityGraphViewState extends State<_EntityGraphView>
|
|
|
2778
2788
|
for (int i = 0; i < entities.length; i++) {
|
|
2779
2789
|
final entity = entities[i];
|
|
2780
2790
|
final sizeFactor = 0.4 + 0.6 * (entity.mentionCount / maxMention);
|
|
2781
|
-
_nodes.add(
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2791
|
+
_nodes.add(
|
|
2792
|
+
_GraphNode(
|
|
2793
|
+
id: entity.name,
|
|
2794
|
+
label: entity.name,
|
|
2795
|
+
radius: 18 + 20 * sizeFactor,
|
|
2796
|
+
color: _kindColors[entity.kind] ?? _kindColors['concept']!,
|
|
2797
|
+
kind: entity.kind,
|
|
2798
|
+
isReflection: false,
|
|
2799
|
+
offsetPhase: i * 0.7,
|
|
2800
|
+
),
|
|
2801
|
+
);
|
|
2790
2802
|
}
|
|
2791
2803
|
|
|
2792
2804
|
for (int i = 0; i < views.length && i < 6; i++) {
|
|
2793
|
-
_nodes.add(
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2805
|
+
_nodes.add(
|
|
2806
|
+
_GraphNode(
|
|
2807
|
+
id: 'kv_${views[i].title}',
|
|
2808
|
+
label: views[i].title,
|
|
2809
|
+
radius: 14,
|
|
2810
|
+
color: const Color(0xFF8B7EC8),
|
|
2811
|
+
kind: views[i].viewType,
|
|
2812
|
+
isReflection: true,
|
|
2813
|
+
offsetPhase: (entities.length + i) * 0.9,
|
|
2814
|
+
),
|
|
2815
|
+
);
|
|
2802
2816
|
}
|
|
2803
2817
|
|
|
2804
2818
|
_layoutDone = false;
|
|
@@ -2939,10 +2953,12 @@ class _EntityGraphPainter extends CustomPainter {
|
|
|
2939
2953
|
void paint(Canvas canvas, Size size) {
|
|
2940
2954
|
if (nodes.isEmpty) return;
|
|
2941
2955
|
|
|
2942
|
-
final entityNodes =
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2956
|
+
final entityNodes = nodes
|
|
2957
|
+
.where((n) => !n.isReflection)
|
|
2958
|
+
.toList(growable: false);
|
|
2959
|
+
final reflectionNodes = nodes
|
|
2960
|
+
.where((n) => n.isReflection)
|
|
2961
|
+
.toList(growable: false);
|
|
2946
2962
|
|
|
2947
2963
|
// Draw connections between entity nodes (subtle web)
|
|
2948
2964
|
final linePaint = Paint()
|
|
@@ -2974,8 +2990,8 @@ class _EntityGraphPainter extends CustomPainter {
|
|
|
2974
2990
|
var closest = entityNodes.first;
|
|
2975
2991
|
var minDist = double.infinity;
|
|
2976
2992
|
for (final en in entityNodes) {
|
|
2977
|
-
final d =
|
|
2978
|
-
(en.y - rn.y) * (en.y - rn.y);
|
|
2993
|
+
final d =
|
|
2994
|
+
(en.x - rn.x) * (en.x - rn.x) + (en.y - rn.y) * (en.y - rn.y);
|
|
2979
2995
|
if (d < minDist) {
|
|
2980
2996
|
minDist = d;
|
|
2981
2997
|
closest = en;
|
|
@@ -3002,8 +3018,9 @@ class _EntityGraphPainter extends CustomPainter {
|
|
|
3002
3018
|
// Glow
|
|
3003
3019
|
if (isSelected || isHovered) {
|
|
3004
3020
|
final glowPaint = Paint()
|
|
3005
|
-
..color = (isSelected ? accentColor : node.color)
|
|
3006
|
-
|
|
3021
|
+
..color = (isSelected ? accentColor : node.color).withValues(
|
|
3022
|
+
alpha: 0.22,
|
|
3023
|
+
)
|
|
3007
3024
|
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 12);
|
|
3008
3025
|
canvas.drawCircle(Offset(cx, cy), r + 6, glowPaint);
|
|
3009
3026
|
}
|
|
@@ -3023,8 +3040,8 @@ class _EntityGraphPainter extends CustomPainter {
|
|
|
3023
3040
|
..color = isSelected
|
|
3024
3041
|
? accentColor
|
|
3025
3042
|
: (isHovered
|
|
3026
|
-
|
|
3027
|
-
|
|
3043
|
+
? node.color.withValues(alpha: 0.8)
|
|
3044
|
+
: node.color.withValues(alpha: 0.35))
|
|
3028
3045
|
..style = PaintingStyle.stroke
|
|
3029
3046
|
..strokeWidth = isSelected ? 2.5 : 1.5;
|
|
3030
3047
|
canvas.drawCircle(Offset(cx, cy), r, borderPaint);
|
|
@@ -3041,10 +3058,7 @@ class _EntityGraphPainter extends CustomPainter {
|
|
|
3041
3058
|
maxLines: 1,
|
|
3042
3059
|
ellipsis: '…',
|
|
3043
3060
|
)..layout(maxWidth: r * 3);
|
|
3044
|
-
tp.paint(
|
|
3045
|
-
canvas,
|
|
3046
|
-
Offset(cx - tp.width / 2, cy + r + 5),
|
|
3047
|
-
);
|
|
3061
|
+
tp.paint(canvas, Offset(cx - tp.width / 2, cy + r + 5));
|
|
3048
3062
|
}
|
|
3049
3063
|
}
|
|
3050
3064
|
|
|
@@ -5025,6 +5039,15 @@ const List<_TaskTriggerOption> _taskTriggerOptions = <_TaskTriggerOption>[
|
|
|
5025
5039
|
providerKey: 'google_workspace',
|
|
5026
5040
|
appKey: 'gmail',
|
|
5027
5041
|
),
|
|
5042
|
+
_TaskTriggerOption(
|
|
5043
|
+
type: 'neomail_email_received',
|
|
5044
|
+
section: 'Email',
|
|
5045
|
+
label: 'NeoMail Email Received',
|
|
5046
|
+
description: 'Run when a matching message is synced into NeoMail.',
|
|
5047
|
+
icon: Icons.mark_email_unread_rounded,
|
|
5048
|
+
providerKey: 'neomail',
|
|
5049
|
+
appKey: 'mailbox',
|
|
5050
|
+
),
|
|
5028
5051
|
_TaskTriggerOption(
|
|
5029
5052
|
type: 'outlook_email_received',
|
|
5030
5053
|
section: 'Email',
|
|
@@ -5087,7 +5110,6 @@ _TaskTriggerOption _taskTriggerOptionForType(String type) {
|
|
|
5087
5110
|
);
|
|
5088
5111
|
}
|
|
5089
5112
|
|
|
5090
|
-
|
|
5091
5113
|
Future<String?> _pickTaskTriggerType(
|
|
5092
5114
|
BuildContext context,
|
|
5093
5115
|
String selectedType,
|
|
@@ -5847,9 +5869,7 @@ class _TasksPanelState extends State<TasksPanel> {
|
|
|
5847
5869
|
final selectedConnectionId = ValueNotifier<int?>(
|
|
5848
5870
|
task?.triggerConfig['connectionId'] is int
|
|
5849
5871
|
? task!.triggerConfig['connectionId'] as int
|
|
5850
|
-
: int.tryParse(
|
|
5851
|
-
task?.triggerConfig['connectionId']?.toString() ?? '',
|
|
5852
|
-
),
|
|
5872
|
+
: int.tryParse(task?.triggerConfig['connectionId']?.toString() ?? ''),
|
|
5853
5873
|
);
|
|
5854
5874
|
final queryController = TextEditingController(
|
|
5855
5875
|
text:
|
|
@@ -5869,6 +5889,8 @@ class _TasksPanelState extends State<TasksPanel> {
|
|
|
5869
5889
|
);
|
|
5870
5890
|
final channelController = TextEditingController(
|
|
5871
5891
|
text:
|
|
5892
|
+
task?.triggerConfig['folder']?.toString() ??
|
|
5893
|
+
task?.triggerConfig['folderId']?.toString() ??
|
|
5872
5894
|
task?.triggerConfig['channel']?.toString() ??
|
|
5873
5895
|
task?.triggerConfig['chatId']?.toString() ??
|
|
5874
5896
|
'',
|
|
@@ -6089,6 +6111,8 @@ class _TasksPanelState extends State<TasksPanel> {
|
|
|
6089
6111
|
],
|
|
6090
6112
|
if (selectedTriggerType ==
|
|
6091
6113
|
'gmail_message_received' ||
|
|
6114
|
+
selectedTriggerType ==
|
|
6115
|
+
'neomail_email_received' ||
|
|
6092
6116
|
selectedTriggerType ==
|
|
6093
6117
|
'outlook_email_received') ...<Widget>[
|
|
6094
6118
|
TextField(
|
|
@@ -6106,6 +6130,18 @@ class _TasksPanelState extends State<TasksPanel> {
|
|
|
6106
6130
|
setLocalState(() => unreadOnly = value),
|
|
6107
6131
|
),
|
|
6108
6132
|
],
|
|
6133
|
+
if (selectedTriggerType ==
|
|
6134
|
+
'neomail_email_received') ...<Widget>[
|
|
6135
|
+
TextField(
|
|
6136
|
+
controller: channelController,
|
|
6137
|
+
decoration: const InputDecoration(
|
|
6138
|
+
labelText: 'Folder (optional)',
|
|
6139
|
+
helperText:
|
|
6140
|
+
'Example: INBOX, archive, or a custom folder path.',
|
|
6141
|
+
),
|
|
6142
|
+
),
|
|
6143
|
+
const SizedBox(height: 12),
|
|
6144
|
+
],
|
|
6109
6145
|
if (selectedTriggerType ==
|
|
6110
6146
|
'outlook_email_received') ...<Widget>[
|
|
6111
6147
|
TextField(
|
|
@@ -6362,11 +6398,17 @@ class _TasksPanelState extends State<TasksPanel> {
|
|
|
6362
6398
|
triggerConfig['eventTypes'] = eventTypes;
|
|
6363
6399
|
}
|
|
6364
6400
|
if (selectedTriggerType == 'gmail_message_received' ||
|
|
6401
|
+
selectedTriggerType == 'neomail_email_received' ||
|
|
6365
6402
|
selectedTriggerType == 'outlook_email_received') {
|
|
6366
6403
|
if (queryController.text.trim().isNotEmpty) {
|
|
6367
6404
|
triggerConfig['query'] = queryController.text.trim();
|
|
6368
6405
|
}
|
|
6369
6406
|
triggerConfig['unreadOnly'] = unreadOnly;
|
|
6407
|
+
if (selectedTriggerType == 'neomail_email_received' &&
|
|
6408
|
+
channelController.text.trim().isNotEmpty) {
|
|
6409
|
+
triggerConfig['folder'] = channelController.text
|
|
6410
|
+
.trim();
|
|
6411
|
+
}
|
|
6370
6412
|
if (selectedTriggerType == 'outlook_email_received' &&
|
|
6371
6413
|
channelController.text.trim().isNotEmpty) {
|
|
6372
6414
|
triggerConfig['folderId'] = channelController.text
|