neoagent 3.3.1-beta.2 → 3.3.1-beta.3
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/README.md +6 -1
- package/docs/getting-started.md +11 -8
- package/docs/index.md +5 -3
- package/flutter_app/lib/main_settings.dart +214 -101
- package/flutter_app/lib/main_unified.dart +178 -76
- package/lib/manager.js +253 -17
- package/package.json +1 -1
- package/runtime/paths.js +2 -0
- package/server/db/database.js +6 -2
- 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 +56371 -56240
- package/server/services/ai/provider_definitions.js +140 -0
- package/server/services/ai/settings.js +1 -123
package/README.md
CHANGED
|
@@ -49,9 +49,14 @@ is available.
|
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
npm install -g neoagent
|
|
52
|
-
neoagent
|
|
52
|
+
neoagent
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
On its first run, `neoagent` scans the computer for an existing instance. If
|
|
56
|
+
none is found, it installs the application, prepares the service and isolated
|
|
57
|
+
runtime, then opens a focused terminal wizard for your AI provider API keys.
|
|
58
|
+
Running `neoagent` again shows the status of the existing installation.
|
|
59
|
+
|
|
55
60
|
<p align="center">
|
|
56
61
|
<a href="https://github.com/NeoLabs-Systems/NeoAgent/releases/latest"><img alt="Download macOS app" src="https://img.shields.io/badge/macOS_app-download-black?style=flat-square&logo=apple&logoColor=white"></a>
|
|
57
62
|
</p>
|
package/docs/getting-started.md
CHANGED
|
@@ -29,12 +29,15 @@ the cloud browser/CLI become available once Docker is running.
|
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
npm install -g neoagent
|
|
32
|
-
neoagent
|
|
32
|
+
neoagent
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
`neoagent
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
The first `neoagent` run checks the host for an existing installation. On a new
|
|
36
|
+
machine it creates the runtime configuration, installs application dependencies
|
|
37
|
+
and supported system tools, prepares the isolated runtime, guides you through
|
|
38
|
+
the API keys for the hosted providers you choose, and registers a launchd or
|
|
39
|
+
systemd user service when available. Later runs show the existing
|
|
40
|
+
installation's status.
|
|
38
41
|
|
|
39
42
|
Read the post-install actions printed by the command. They describe anything
|
|
40
43
|
the installer could not complete automatically.
|
|
@@ -45,10 +48,10 @@ and `PUBLIC_URL` before connecting integrations or mobile clients.
|
|
|
45
48
|
## First run
|
|
46
49
|
|
|
47
50
|
1. Create the first user account.
|
|
48
|
-
2. Open **Settings > AI Providers
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
2. Open **Settings > AI Providers** and verify the provider configured by the
|
|
52
|
+
terminal wizard, or connect local Ollama.
|
|
53
|
+
3. Select a model and send a message in **Chat**.
|
|
54
|
+
4. Open **Settings > Tool Permissions** and review the approval policy before
|
|
52
55
|
enabling browser, shell, file-write, desktop, or Android actions.
|
|
53
56
|
|
|
54
57
|
The first isolated-runtime boot downloads and prepares an Ubuntu image. It can
|
package/docs/index.md
CHANGED
|
@@ -18,11 +18,13 @@ sensitive accounts.
|
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
npm install -g neoagent
|
|
21
|
-
neoagent
|
|
21
|
+
neoagent
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
On a new machine, the bare command detects that NeoAgent is not installed,
|
|
25
|
+
runs the installer, and guides you through AI provider API keys. It starts the
|
|
26
|
+
service and prints any host-specific action items. Open
|
|
27
|
+
`http://localhost:3333` when it finishes.
|
|
26
28
|
|
|
27
29
|
Continue with:
|
|
28
30
|
|
|
@@ -79,116 +79,143 @@ const Map<String, List<String>> _voiceLiveVoicesByProvider =
|
|
|
79
79
|
class _SettingsSection {
|
|
80
80
|
const _SettingsSection(
|
|
81
81
|
this.title,
|
|
82
|
+
this.label,
|
|
83
|
+
this.icon,
|
|
82
84
|
this.keywords, {
|
|
83
85
|
this.requiresDesktop = false,
|
|
84
86
|
});
|
|
85
87
|
|
|
86
88
|
final String title;
|
|
89
|
+
final String label;
|
|
90
|
+
final IconData icon;
|
|
87
91
|
final List<String> keywords;
|
|
88
92
|
final bool requiresDesktop;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
const _overviewSettingsSection = _SettingsSection(
|
|
95
|
+
const _overviewSettingsSection = _SettingsSection(
|
|
92
96
|
'overview',
|
|
93
|
-
'
|
|
94
|
-
|
|
95
|
-
'platform',
|
|
96
|
-
|
|
97
|
-
]);
|
|
97
|
+
'Overview',
|
|
98
|
+
Icons.dashboard_outlined,
|
|
99
|
+
<String>['overview', 'summary', 'onboarding', 'platform', 'providers'],
|
|
100
|
+
);
|
|
98
101
|
|
|
99
|
-
const _workspaceSettingsSection = _SettingsSection(
|
|
102
|
+
const _workspaceSettingsSection = _SettingsSection(
|
|
100
103
|
'workspace',
|
|
101
|
-
'
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
'Workspace',
|
|
105
|
+
Icons.workspaces_outline,
|
|
106
|
+
<String>[
|
|
107
|
+
'workspace',
|
|
108
|
+
'browser',
|
|
109
|
+
'extension',
|
|
110
|
+
'cli',
|
|
111
|
+
'claude code',
|
|
112
|
+
'desktop',
|
|
113
|
+
'routing',
|
|
114
|
+
],
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const _behaviorSettingsSection = _SettingsSection(
|
|
110
118
|
'behavior',
|
|
111
|
-
'
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
'
|
|
140
|
-
'
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
119
|
+
'Behavior',
|
|
120
|
+
Icons.psychology_outlined,
|
|
121
|
+
<String>[
|
|
122
|
+
'behavior',
|
|
123
|
+
'persona',
|
|
124
|
+
'social intelligence',
|
|
125
|
+
'turn taking',
|
|
126
|
+
'groups',
|
|
127
|
+
'memory',
|
|
128
|
+
'norms',
|
|
129
|
+
'theory of mind',
|
|
130
|
+
'delivery',
|
|
131
|
+
],
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const _modelsSettingsSection =
|
|
135
|
+
_SettingsSection('models', 'Models & routing', Icons.hub_outlined, <String>[
|
|
136
|
+
'models',
|
|
137
|
+
'providers',
|
|
138
|
+
'routing',
|
|
139
|
+
'fallback',
|
|
140
|
+
'chat',
|
|
141
|
+
'sub-agent',
|
|
142
|
+
'subagent',
|
|
143
|
+
'smart selector',
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
const _socialReachSettingsSection = _SettingsSection(
|
|
147
|
+
'social reach',
|
|
148
|
+
'Social reach',
|
|
149
|
+
Icons.public_outlined,
|
|
150
|
+
<String>[
|
|
151
|
+
'social',
|
|
152
|
+
'reach',
|
|
153
|
+
'web',
|
|
154
|
+
'rss',
|
|
155
|
+
'github',
|
|
156
|
+
'youtube',
|
|
157
|
+
'linkedin',
|
|
158
|
+
'xueqiu',
|
|
159
|
+
'twitter',
|
|
160
|
+
'reddit',
|
|
161
|
+
'instagram',
|
|
162
|
+
'facebook',
|
|
163
|
+
'cookies',
|
|
164
|
+
],
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const _voiceSettingsSection = _SettingsSection(
|
|
149
168
|
'voice',
|
|
150
|
-
'
|
|
151
|
-
|
|
152
|
-
'stt',
|
|
153
|
-
|
|
154
|
-
]);
|
|
169
|
+
'Voice',
|
|
170
|
+
Icons.mic_none_outlined,
|
|
171
|
+
<String>['voice', 'speech', 'tts', 'stt', 'live'],
|
|
172
|
+
);
|
|
155
173
|
|
|
156
|
-
const _desktopSettingsSection = _SettingsSection(
|
|
174
|
+
const _desktopSettingsSection = _SettingsSection(
|
|
157
175
|
'desktop',
|
|
158
|
-
'
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
176
|
+
'Desktop',
|
|
177
|
+
Icons.desktop_windows_outlined,
|
|
178
|
+
<String>[
|
|
179
|
+
'desktop',
|
|
180
|
+
'permissions',
|
|
181
|
+
'capture',
|
|
182
|
+
'companion',
|
|
183
|
+
'screen recording',
|
|
184
|
+
'accessibility',
|
|
185
|
+
'input',
|
|
186
|
+
],
|
|
187
|
+
requiresDesktop: true,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const _diagnosticsSettingsSection = _SettingsSection(
|
|
167
191
|
'diagnostics',
|
|
168
|
-
'
|
|
169
|
-
|
|
170
|
-
'usage',
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const _securitySettingsSection = _SettingsSection('security', <String>[
|
|
192
|
+
'Diagnostics',
|
|
193
|
+
Icons.monitor_heart_outlined,
|
|
194
|
+
<String>['diagnostics', 'logs', 'token', 'usage', 'debug', 'health'],
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const _securitySettingsSection = _SettingsSection(
|
|
176
198
|
'security',
|
|
177
|
-
'
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
199
|
+
'Permissions',
|
|
200
|
+
Icons.admin_panel_settings_outlined,
|
|
201
|
+
<String>[
|
|
202
|
+
'security',
|
|
203
|
+
'tool',
|
|
204
|
+
'permission',
|
|
205
|
+
'allowlist',
|
|
206
|
+
'shell',
|
|
207
|
+
'android',
|
|
208
|
+
'approval',
|
|
209
|
+
'policy',
|
|
210
|
+
],
|
|
211
|
+
);
|
|
185
212
|
|
|
186
213
|
const List<_SettingsSection> _settingsSearchSections = <_SettingsSection>[
|
|
187
214
|
_overviewSettingsSection,
|
|
188
|
-
|
|
215
|
+
_modelsSettingsSection,
|
|
189
216
|
_workspaceSettingsSection,
|
|
217
|
+
_behaviorSettingsSection,
|
|
190
218
|
_socialReachSettingsSection,
|
|
191
|
-
_modelsSettingsSection,
|
|
192
219
|
_voiceSettingsSection,
|
|
193
220
|
_desktopSettingsSection,
|
|
194
221
|
_securitySettingsSection,
|
|
@@ -197,6 +224,7 @@ const List<_SettingsSection> _settingsSearchSections = <_SettingsSection>[
|
|
|
197
224
|
|
|
198
225
|
class _SettingsPanelState extends State<SettingsPanel> {
|
|
199
226
|
late final TextEditingController _searchController;
|
|
227
|
+
_SettingsSection _selectedSettingsSection = _overviewSettingsSection;
|
|
200
228
|
late String _browserBackend;
|
|
201
229
|
String? _browserExtensionTokenId;
|
|
202
230
|
late String _cliBackend;
|
|
@@ -433,11 +461,30 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
433
461
|
trailing: _settingsSaveButton(controller),
|
|
434
462
|
)
|
|
435
463
|
else
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
child:
|
|
439
|
-
|
|
440
|
-
|
|
464
|
+
Padding(
|
|
465
|
+
padding: const EdgeInsets.only(bottom: 12),
|
|
466
|
+
child: Row(
|
|
467
|
+
children: <Widget>[
|
|
468
|
+
Expanded(
|
|
469
|
+
child: Column(
|
|
470
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
471
|
+
children: <Widget>[
|
|
472
|
+
Text(
|
|
473
|
+
'General settings',
|
|
474
|
+
style: Theme.of(context).textTheme.titleLarge
|
|
475
|
+
?.copyWith(fontWeight: FontWeight.w800),
|
|
476
|
+
),
|
|
477
|
+
const SizedBox(height: 2),
|
|
478
|
+
Text(
|
|
479
|
+
'Choose a category or search across all settings.',
|
|
480
|
+
style: TextStyle(color: _textSecondary),
|
|
481
|
+
),
|
|
482
|
+
],
|
|
483
|
+
),
|
|
484
|
+
),
|
|
485
|
+
const SizedBox(width: 12),
|
|
486
|
+
_settingsSaveButton(controller),
|
|
487
|
+
],
|
|
441
488
|
),
|
|
442
489
|
),
|
|
443
490
|
if (controller.errorMessage != null) ...<Widget>[
|
|
@@ -462,36 +509,38 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
462
509
|
),
|
|
463
510
|
),
|
|
464
511
|
),
|
|
512
|
+
const SizedBox(height: 12),
|
|
513
|
+
_buildSettingsCategoryPicker(visibleSearchSections),
|
|
465
514
|
const SizedBox(height: 16),
|
|
466
|
-
if (
|
|
515
|
+
if (_showsSettingsSection(
|
|
467
516
|
searchQuery,
|
|
468
517
|
_overviewSettingsSection,
|
|
469
518
|
)) ...<Widget>[
|
|
470
519
|
_buildSettingsOverview(controller, availableModels.length),
|
|
471
520
|
const SizedBox(height: 16),
|
|
472
521
|
],
|
|
473
|
-
if (
|
|
522
|
+
if (_showsSettingsSection(
|
|
474
523
|
searchQuery,
|
|
475
524
|
_behaviorSettingsSection,
|
|
476
525
|
)) ...<Widget>[
|
|
477
526
|
_buildBehaviorSection(controller, routingModels),
|
|
478
527
|
const SizedBox(height: 16),
|
|
479
528
|
],
|
|
480
|
-
if (
|
|
529
|
+
if (_showsSettingsSection(
|
|
481
530
|
searchQuery,
|
|
482
531
|
_workspaceSettingsSection,
|
|
483
532
|
)) ...<Widget>[
|
|
484
533
|
_buildWorkspaceSection(controller),
|
|
485
534
|
const SizedBox(height: 16),
|
|
486
535
|
],
|
|
487
|
-
if (
|
|
536
|
+
if (_showsSettingsSection(
|
|
488
537
|
searchQuery,
|
|
489
538
|
_socialReachSettingsSection,
|
|
490
539
|
)) ...<Widget>[
|
|
491
540
|
_buildSocialReachSection(controller),
|
|
492
541
|
const SizedBox(height: 16),
|
|
493
542
|
],
|
|
494
|
-
if (
|
|
543
|
+
if (_showsSettingsSection(
|
|
495
544
|
searchQuery,
|
|
496
545
|
_modelsSettingsSection,
|
|
497
546
|
)) ...<Widget>[
|
|
@@ -505,7 +554,7 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
505
554
|
),
|
|
506
555
|
const SizedBox(height: 16),
|
|
507
556
|
],
|
|
508
|
-
if (
|
|
557
|
+
if (_showsSettingsSection(
|
|
509
558
|
searchQuery,
|
|
510
559
|
_voiceSettingsSection,
|
|
511
560
|
)) ...<Widget>[
|
|
@@ -517,21 +566,21 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
517
566
|
const SizedBox(height: 16),
|
|
518
567
|
],
|
|
519
568
|
if (visibleSearchSections.contains(_desktopSettingsSection) &&
|
|
520
|
-
|
|
569
|
+
_showsSettingsSection(
|
|
521
570
|
searchQuery,
|
|
522
571
|
_desktopSettingsSection,
|
|
523
572
|
)) ...<Widget>[
|
|
524
573
|
_buildDesktopSection(controller),
|
|
525
574
|
const SizedBox(height: 16),
|
|
526
575
|
],
|
|
527
|
-
if (
|
|
576
|
+
if (_showsSettingsSection(
|
|
528
577
|
searchQuery,
|
|
529
578
|
_securitySettingsSection,
|
|
530
579
|
)) ...<Widget>[
|
|
531
580
|
_buildSecuritySection(context, controller),
|
|
532
581
|
const SizedBox(height: 16),
|
|
533
582
|
],
|
|
534
|
-
if (
|
|
583
|
+
if (_showsSettingsSection(
|
|
535
584
|
searchQuery,
|
|
536
585
|
_diagnosticsSettingsSection,
|
|
537
586
|
)) ...<Widget>[_buildDiagnosticsSection(controller)],
|
|
@@ -560,6 +609,70 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
560
609
|
return haystack.contains(query);
|
|
561
610
|
}
|
|
562
611
|
|
|
612
|
+
bool _showsSettingsSection(String query, _SettingsSection section) {
|
|
613
|
+
if (query.isNotEmpty) {
|
|
614
|
+
return _matchesSettingsSection(query, section);
|
|
615
|
+
}
|
|
616
|
+
return _selectedSettingsSection == section;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
Widget _buildSettingsCategoryPicker(Set<_SettingsSection> visibleSections) {
|
|
620
|
+
final sections = _settingsSearchSections
|
|
621
|
+
.where(visibleSections.contains)
|
|
622
|
+
.toList();
|
|
623
|
+
return LayoutBuilder(
|
|
624
|
+
builder: (context, constraints) {
|
|
625
|
+
if (constraints.maxWidth < 620) {
|
|
626
|
+
return DropdownButtonFormField<_SettingsSection>(
|
|
627
|
+
key: ValueKey<_SettingsSection>(_selectedSettingsSection),
|
|
628
|
+
initialValue: _selectedSettingsSection,
|
|
629
|
+
isExpanded: true,
|
|
630
|
+
decoration: const InputDecoration(
|
|
631
|
+
labelText: 'Category',
|
|
632
|
+
prefixIcon: Icon(Icons.category_outlined),
|
|
633
|
+
),
|
|
634
|
+
items: sections
|
|
635
|
+
.map(
|
|
636
|
+
(section) => DropdownMenuItem<_SettingsSection>(
|
|
637
|
+
value: section,
|
|
638
|
+
child: Row(
|
|
639
|
+
children: <Widget>[
|
|
640
|
+
Icon(section.icon, size: 18),
|
|
641
|
+
const SizedBox(width: 10),
|
|
642
|
+
Text(section.label),
|
|
643
|
+
],
|
|
644
|
+
),
|
|
645
|
+
),
|
|
646
|
+
)
|
|
647
|
+
.toList(),
|
|
648
|
+
onChanged: (section) {
|
|
649
|
+
if (section == null) return;
|
|
650
|
+
_searchController.clear();
|
|
651
|
+
setState(() => _selectedSettingsSection = section);
|
|
652
|
+
},
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
return Wrap(
|
|
656
|
+
spacing: 8,
|
|
657
|
+
runSpacing: 8,
|
|
658
|
+
children: sections
|
|
659
|
+
.map(
|
|
660
|
+
(section) => ChoiceChip(
|
|
661
|
+
avatar: Icon(section.icon, size: 17),
|
|
662
|
+
label: Text(section.label),
|
|
663
|
+
selected: _selectedSettingsSection == section,
|
|
664
|
+
onSelected: (_) {
|
|
665
|
+
_searchController.clear();
|
|
666
|
+
setState(() => _selectedSettingsSection = section);
|
|
667
|
+
},
|
|
668
|
+
),
|
|
669
|
+
)
|
|
670
|
+
.toList(),
|
|
671
|
+
);
|
|
672
|
+
},
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
|
|
563
676
|
bool _noSettingsMatches(
|
|
564
677
|
String query,
|
|
565
678
|
Iterable<_SettingsSection> visibleSections,
|