neoagent 2.3.1-beta.63 → 2.3.1-beta.65
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/docs/capabilities.md +1 -1
- package/docs/configuration.md +2 -2
- package/flutter_app/lib/main.dart +1 -0
- package/flutter_app/lib/main_account_settings.dart +50 -22
- package/flutter_app/lib/main_admin.dart +24 -10
- package/flutter_app/lib/main_app_shell.dart +10 -9
- package/flutter_app/lib/main_chat.dart +631 -318
- package/flutter_app/lib/main_controller.dart +29 -8
- package/flutter_app/lib/main_devices.dart +4 -6
- package/flutter_app/lib/main_integrations.dart +15 -7
- package/flutter_app/lib/main_models.dart +207 -8
- package/flutter_app/lib/main_navigation.dart +36 -18
- package/flutter_app/lib/main_operations.dart +162 -91
- package/flutter_app/lib/main_settings.dart +273 -78
- package/flutter_app/lib/main_shared.dart +8 -6
- package/flutter_app/lib/main_unified.dart +388 -0
- package/package.json +1 -1
- package/server/db/database.js +52 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/AssetManifest.json +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 +79074 -78010
- package/server/routes/agents.js +2 -1
- package/server/routes/browser.js +1 -14
- package/server/routes/memory.js +75 -3
- package/server/routes/settings.js +1 -5
- package/server/routes/widgets.js +4 -4
- package/server/services/ai/capabilityHealth.js +1 -10
- package/server/services/ai/deliverables/artifact_helpers.js +190 -0
- package/server/services/ai/deliverables/contracts.js +113 -0
- package/server/services/ai/deliverables/deliverables.test.js +76 -0
- package/server/services/ai/deliverables/index.js +20 -0
- package/server/services/ai/deliverables/selector.js +94 -0
- package/server/services/ai/deliverables/validator.js +63 -0
- package/server/services/ai/deliverables/workflows.js +195 -0
- package/server/services/ai/engine.js +259 -1
- package/server/services/ai/runEvents.js +100 -0
- package/server/services/ai/systemPrompt.js +6 -0
- package/server/services/ai/tools.js +1 -5
- package/server/services/manager.js +5 -56
- package/server/services/memory/manager.js +242 -26
- package/server/services/runtime/manager.js +2 -6
- package/server/services/runtime/settings.js +6 -12
- package/server/services/websocket.js +3 -1
- package/server/services/widgets/focus_widget.js +134 -0
- package/server/services/widgets/service.js +130 -2
- package/server/utils/deployment.js +4 -3
package/docs/capabilities.md
CHANGED
|
@@ -112,7 +112,7 @@ Runtime settings let operators choose where higher-risk work runs:
|
|
|
112
112
|
|
|
113
113
|
| Profile | Runtime shape |
|
|
114
114
|
|---|---|
|
|
115
|
-
| `trusted-host` | CLI
|
|
115
|
+
| `trusted-host` | CLI and Android tools run on the host; browser runs in the VM or paired extension |
|
|
116
116
|
| `secure-vm` | CLI, browser, and Android tools run through the local VM backend |
|
|
117
117
|
|
|
118
118
|
Production policy can require the secure VM profile and a strong VM guest token.
|
package/docs/configuration.md
CHANGED
|
@@ -130,11 +130,11 @@ Telnyx webhook verification is configured through the environment.
|
|
|
130
130
|
|
|
131
131
|
## Runtime Isolation
|
|
132
132
|
|
|
133
|
-
Runtime profile and backend selection are stored in user settings, not normally in `.env`. The main profiles are `trusted-host` and `secure-vm`.
|
|
133
|
+
Runtime profile and backend selection are stored in user settings, not normally in `.env`. The main profiles are `trusted-host` and `secure-vm`. CLI and Android tools may still follow the selected runtime profile, but browser control is always isolated: the non-extension browser backend runs in the VM, not on the host device.
|
|
134
134
|
|
|
135
135
|
Production policy can require the VM backend. In that case, set a strong `NEOAGENT_VM_GUEST_TOKEN` of at least 32 characters and avoid placeholder values.
|
|
136
136
|
|
|
137
|
-
The app exposes two browser backend choices:
|
|
137
|
+
The app exposes two browser backend choices: VM and Chrome extension. VM uses the local isolated runtime. Chrome extension uses the paired extension connection on the remote machine instead of the server-local browser. To install only the extension on a remote machine, open NeoAgent, download `/api/browser-extension/download`, unzip it, load the folder through `chrome://extensions` with Developer mode enabled, then pair after logging in to NeoAgent. Unpacked Chrome extensions cannot replace themselves automatically; use the extension popup's update check to compare against the server bundle, then download and reload the latest ZIP when needed.
|
|
138
138
|
|
|
139
139
|
## Secrets Guidance
|
|
140
140
|
|
|
@@ -150,16 +150,23 @@ class _PasswordStrengthIndicator extends StatelessWidget {
|
|
|
150
150
|
enum AccountSettingsTab { account, security }
|
|
151
151
|
|
|
152
152
|
class AccountSettingsPanel extends StatefulWidget {
|
|
153
|
-
const AccountSettingsPanel({
|
|
153
|
+
const AccountSettingsPanel({
|
|
154
|
+
super.key,
|
|
155
|
+
required this.controller,
|
|
156
|
+
this.embedded = false,
|
|
157
|
+
this.initialTab,
|
|
158
|
+
});
|
|
154
159
|
|
|
155
160
|
final NeoAgentController controller;
|
|
161
|
+
final bool embedded;
|
|
162
|
+
final AccountSettingsTab? initialTab;
|
|
156
163
|
|
|
157
164
|
@override
|
|
158
165
|
State<AccountSettingsPanel> createState() => _AccountSettingsPanelState();
|
|
159
166
|
}
|
|
160
167
|
|
|
161
168
|
class _AccountSettingsPanelState extends State<AccountSettingsPanel> {
|
|
162
|
-
AccountSettingsTab _selectedTab
|
|
169
|
+
late AccountSettingsTab _selectedTab;
|
|
163
170
|
late final TextEditingController _emailController;
|
|
164
171
|
late final TextEditingController _emailPasswordController;
|
|
165
172
|
late final TextEditingController _setupPasswordController;
|
|
@@ -179,6 +186,7 @@ class _AccountSettingsPanelState extends State<AccountSettingsPanel> {
|
|
|
179
186
|
@override
|
|
180
187
|
void initState() {
|
|
181
188
|
super.initState();
|
|
189
|
+
_selectedTab = widget.initialTab ?? AccountSettingsTab.account;
|
|
182
190
|
_emailController = TextEditingController(
|
|
183
191
|
text: widget.controller.user?['email']?.toString() ?? '',
|
|
184
192
|
);
|
|
@@ -196,6 +204,10 @@ class _AccountSettingsPanelState extends State<AccountSettingsPanel> {
|
|
|
196
204
|
@override
|
|
197
205
|
void didUpdateWidget(covariant AccountSettingsPanel oldWidget) {
|
|
198
206
|
super.didUpdateWidget(oldWidget);
|
|
207
|
+
if (widget.initialTab != null &&
|
|
208
|
+
oldWidget.initialTab != widget.initialTab) {
|
|
209
|
+
_selectedTab = widget.initialTab!;
|
|
210
|
+
}
|
|
199
211
|
final email = widget.controller.user?['email']?.toString() ?? '';
|
|
200
212
|
if (_emailController.text.isEmpty && email.isNotEmpty) {
|
|
201
213
|
_emailController.text = email;
|
|
@@ -291,42 +303,43 @@ class _AccountSettingsPanelState extends State<AccountSettingsPanel> {
|
|
|
291
303
|
@override
|
|
292
304
|
Widget build(BuildContext context) {
|
|
293
305
|
final compact = MediaQuery.sizeOf(context).width < 860;
|
|
306
|
+
final showTabSwitcher = widget.initialTab == null;
|
|
294
307
|
return ListView(
|
|
295
|
-
padding: _pagePadding(context),
|
|
308
|
+
padding: widget.embedded ? EdgeInsets.zero : _pagePadding(context),
|
|
296
309
|
children: <Widget>[
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
label: Text('Refresh'),
|
|
310
|
+
if (!widget.embedded)
|
|
311
|
+
_PageTitle(
|
|
312
|
+
title: 'Account settings',
|
|
313
|
+
subtitle:
|
|
314
|
+
'Manage your account email, two-factor authentication, and active sessions.',
|
|
315
|
+
trailing: _refreshButton(),
|
|
316
|
+
)
|
|
317
|
+
else
|
|
318
|
+
Align(
|
|
319
|
+
alignment: Alignment.centerRight,
|
|
320
|
+
child: Padding(
|
|
321
|
+
padding: const EdgeInsets.only(bottom: 12),
|
|
322
|
+
child: _refreshButton(),
|
|
323
|
+
),
|
|
312
324
|
),
|
|
313
|
-
),
|
|
314
325
|
if (widget.controller.errorMessage != null) ...<Widget>[
|
|
315
326
|
_InlineError(message: widget.controller.errorMessage!),
|
|
316
327
|
const SizedBox(height: 16),
|
|
317
328
|
],
|
|
318
|
-
if (compact)
|
|
329
|
+
if (showTabSwitcher && compact)
|
|
319
330
|
_AccountSettingsTabs(
|
|
320
331
|
selected: _selectedTab,
|
|
321
332
|
onSelected: (value) => setState(() => _selectedTab = value),
|
|
322
333
|
)
|
|
323
334
|
else
|
|
324
335
|
const SizedBox.shrink(),
|
|
325
|
-
if (compact) const SizedBox(height: 16),
|
|
336
|
+
if (showTabSwitcher && compact) const SizedBox(height: 16),
|
|
326
337
|
Card(
|
|
327
338
|
child: Padding(
|
|
328
339
|
padding: const EdgeInsets.all(20),
|
|
329
|
-
child:
|
|
340
|
+
child: !showTabSwitcher
|
|
341
|
+
? _buildSelectedPanel()
|
|
342
|
+
: compact
|
|
330
343
|
? _buildSelectedPanel()
|
|
331
344
|
: Row(
|
|
332
345
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
@@ -359,6 +372,21 @@ class _AccountSettingsPanelState extends State<AccountSettingsPanel> {
|
|
|
359
372
|
}
|
|
360
373
|
}
|
|
361
374
|
|
|
375
|
+
Widget _refreshButton() {
|
|
376
|
+
return OutlinedButton.icon(
|
|
377
|
+
onPressed: widget.controller.isLoadingAccountSettings
|
|
378
|
+
? null
|
|
379
|
+
: widget.controller.refreshAccountSettings,
|
|
380
|
+
icon: widget.controller.isLoadingAccountSettings
|
|
381
|
+
? const SizedBox.square(
|
|
382
|
+
dimension: 16,
|
|
383
|
+
child: CircularProgressIndicator(strokeWidth: 2),
|
|
384
|
+
)
|
|
385
|
+
: Icon(Icons.refresh),
|
|
386
|
+
label: Text('Refresh'),
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
362
390
|
Widget _buildAccountPanel() {
|
|
363
391
|
final controller = widget.controller;
|
|
364
392
|
final username = controller.user?['username']?.toString() ?? 'Account';
|
|
@@ -367,24 +367,38 @@ class AgentsPanel extends StatelessWidget {
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
class McpPanel extends StatelessWidget {
|
|
370
|
-
const McpPanel({super.key, required this.controller});
|
|
370
|
+
const McpPanel({super.key, required this.controller, this.embedded = false});
|
|
371
371
|
|
|
372
372
|
final NeoAgentController controller;
|
|
373
|
+
final bool embedded;
|
|
373
374
|
|
|
374
375
|
@override
|
|
375
376
|
Widget build(BuildContext context) {
|
|
376
377
|
return ListView(
|
|
377
|
-
padding: _pagePadding(context),
|
|
378
|
+
padding: embedded ? EdgeInsets.zero : _pagePadding(context),
|
|
378
379
|
children: <Widget>[
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
380
|
+
if (!embedded)
|
|
381
|
+
_PageTitle(
|
|
382
|
+
title: 'MCP',
|
|
383
|
+
subtitle: 'Configured MCP servers and live server status.',
|
|
384
|
+
trailing: FilledButton.icon(
|
|
385
|
+
onPressed: () => _openMcpEditor(context),
|
|
386
|
+
icon: Icon(Icons.add),
|
|
387
|
+
label: Text('Add Server'),
|
|
388
|
+
),
|
|
389
|
+
)
|
|
390
|
+
else
|
|
391
|
+
Align(
|
|
392
|
+
alignment: Alignment.centerRight,
|
|
393
|
+
child: Padding(
|
|
394
|
+
padding: const EdgeInsets.only(bottom: 12),
|
|
395
|
+
child: FilledButton.icon(
|
|
396
|
+
onPressed: () => _openMcpEditor(context),
|
|
397
|
+
icon: const Icon(Icons.add),
|
|
398
|
+
label: const Text('Add Server'),
|
|
399
|
+
),
|
|
400
|
+
),
|
|
386
401
|
),
|
|
387
|
-
),
|
|
388
402
|
if (controller.mcpServers.isEmpty)
|
|
389
403
|
const _EmptyCard(
|
|
390
404
|
title: 'No MCP servers configured',
|
|
@@ -1120,10 +1120,11 @@ class _HomeViewState extends State<HomeView> {
|
|
|
1120
1120
|
}
|
|
1121
1121
|
|
|
1122
1122
|
SidebarGroup? _sidebarGroupForSection(AppSection section) {
|
|
1123
|
-
|
|
1123
|
+
final visibleSection = section.sidebarSection;
|
|
1124
|
+
if (!_mainSections(widget.controller).contains(visibleSection)) {
|
|
1124
1125
|
return null;
|
|
1125
1126
|
}
|
|
1126
|
-
return
|
|
1127
|
+
return visibleSection.group;
|
|
1127
1128
|
}
|
|
1128
1129
|
|
|
1129
1130
|
void _handleControllerChanged() {
|
|
@@ -2104,19 +2105,19 @@ class _SectionBody extends StatelessWidget {
|
|
|
2104
2105
|
case AppSection.messaging:
|
|
2105
2106
|
return MessagingPanel(controller: controller);
|
|
2106
2107
|
case AppSection.runs:
|
|
2107
|
-
return
|
|
2108
|
+
return RunsAndLogsPanel(controller: controller);
|
|
2108
2109
|
case AppSection.settings:
|
|
2109
|
-
return
|
|
2110
|
+
return SettingsWorkspacePanel(controller: controller);
|
|
2110
2111
|
case AppSection.accountSettings:
|
|
2111
|
-
return
|
|
2112
|
+
return SettingsWorkspacePanel(controller: controller);
|
|
2112
2113
|
case AppSection.logs:
|
|
2113
|
-
return
|
|
2114
|
+
return RunsAndLogsPanel(controller: controller);
|
|
2114
2115
|
case AppSection.skills:
|
|
2115
|
-
return
|
|
2116
|
+
return ToolsPanel(controller: controller);
|
|
2116
2117
|
case AppSection.agents:
|
|
2117
2118
|
return AgentsPanel(controller: controller);
|
|
2118
2119
|
case AppSection.integrations:
|
|
2119
|
-
return
|
|
2120
|
+
return ToolsPanel(controller: controller);
|
|
2120
2121
|
case AppSection.memory:
|
|
2121
2122
|
return MemoryPanel(controller: controller);
|
|
2122
2123
|
case AppSection.tasks:
|
|
@@ -2124,7 +2125,7 @@ class _SectionBody extends StatelessWidget {
|
|
|
2124
2125
|
case AppSection.widgets:
|
|
2125
2126
|
return WidgetsPanel(controller: controller);
|
|
2126
2127
|
case AppSection.mcp:
|
|
2127
|
-
return
|
|
2128
|
+
return ToolsPanel(controller: controller);
|
|
2128
2129
|
case AppSection.health:
|
|
2129
2130
|
return controller.showHealthSection
|
|
2130
2131
|
? HealthPanel(controller: controller)
|