neoagent 2.4.2-beta.4 → 2.4.2-beta.6
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/.env.example +0 -9
- package/README.md +48 -18
- package/docs/configuration.md +0 -10
- package/flutter_app/lib/main.dart +0 -1
- package/flutter_app/lib/main_controller.dart +0 -160
- package/flutter_app/lib/main_models.dart +28 -0
- package/flutter_app/lib/main_operations.dart +17 -1
- package/flutter_app/lib/main_runtime.dart +0 -22
- package/flutter_app/lib/main_settings.dart +9 -0
- package/flutter_app/lib/main_shared.dart +0 -161
- package/flutter_app/lib/src/backend_client.dart +8 -0
- package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +0 -2
- package/flutter_app/pubspec.lock +0 -8
- package/flutter_app/pubspec.yaml +0 -1
- package/package.json +1 -1
- package/server/admin/admin.css +23 -0
- package/server/admin/admin.js +17 -19
- package/server/admin/analytics.js +254 -48
- package/server/admin/index.html +9 -2
- package/server/db/database.js +112 -0
- package/server/http/routes.js +1 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/AssetManifest.bin +1 -1
- package/server/public/assets/AssetManifest.bin.json +1 -1
- package/server/public/assets/NOTICES +0 -183
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/main.dart.js +77723 -78469
- package/server/routes/admin.js +73 -24
- package/server/routes/mcp.js +9 -0
- package/server/routes/memory.js +35 -2
- package/server/routes/runtime.js +5 -2
- package/server/routes/settings.js +34 -1
- package/server/routes/skills.js +42 -0
- package/server/routes/task_webhooks.js +65 -0
- package/server/services/ai/engine.js +525 -24
- package/server/services/ai/learning.js +56 -8
- package/server/services/ai/models.js +4 -4
- package/server/services/ai/providers/anthropic.js +40 -8
- package/server/services/ai/providers/google.js +10 -0
- package/server/services/ai/providers/openai.js +3 -15
- package/server/services/ai/providers/openaiCompatible.js +11 -0
- package/server/services/ai/repetitionGuard.js +60 -0
- package/server/services/ai/systemPrompt.js +52 -16
- package/server/services/ai/taskAnalysis.js +15 -6
- package/server/services/ai/toolEvidence.js +3 -1
- package/server/services/ai/toolRunner.js +43 -1
- package/server/services/ai/toolSelector.js +92 -46
- package/server/services/ai/tools.js +89 -9
- package/server/services/ai/usage.js +114 -0
- package/server/services/manager.js +34 -0
- package/server/services/memory/manager.js +170 -4
- package/server/services/security/capability_audit.js +107 -0
- package/server/services/tasks/adapters/index.js +1 -0
- package/server/services/tasks/adapters/webhook.js +14 -0
- package/server/services/tasks/runtime.js +1 -1
- package/server/services/tasks/webhooks.js +146 -0
- package/server/services/workspace/code_navigation.js +194 -0
- package/server/services/workspace/structured_data.js +93 -0
- package/server/utils/cloud-security.js +24 -2
- package/flutter_app/lib/src/analytics_service.dart +0 -294
- package/server/config/analytics.js +0 -30
|
@@ -2393,167 +2393,6 @@ class _GlobalWebUpdateBanner extends StatelessWidget {
|
|
|
2393
2393
|
}
|
|
2394
2394
|
}
|
|
2395
2395
|
|
|
2396
|
-
class _GlobalAnalyticsConsentBanner extends StatelessWidget {
|
|
2397
|
-
const _GlobalAnalyticsConsentBanner({required this.controller});
|
|
2398
|
-
|
|
2399
|
-
final NeoAgentController controller;
|
|
2400
|
-
|
|
2401
|
-
@override
|
|
2402
|
-
Widget build(BuildContext context) {
|
|
2403
|
-
return LayoutBuilder(
|
|
2404
|
-
builder: (context, constraints) {
|
|
2405
|
-
final compact = constraints.maxWidth < 700;
|
|
2406
|
-
final body = Text(
|
|
2407
|
-
'NeoAgent uses anonymous analytics to understand startup, navigation, and setup flows. No messages, credentials, or personal content are collected.',
|
|
2408
|
-
style: TextStyle(color: _textSecondary, height: 1.35),
|
|
2409
|
-
);
|
|
2410
|
-
return Material(
|
|
2411
|
-
color: Colors.transparent,
|
|
2412
|
-
child: _GlassSurface(
|
|
2413
|
-
borderRadius: BorderRadius.circular(22),
|
|
2414
|
-
blurSigma: 26,
|
|
2415
|
-
fillColor: _bgCard.withValues(alpha: 0.94),
|
|
2416
|
-
borderColor: _accent.withValues(alpha: 0.18),
|
|
2417
|
-
overlayGradient: LinearGradient(
|
|
2418
|
-
colors: <Color>[
|
|
2419
|
-
_accent.withValues(alpha: 0.12),
|
|
2420
|
-
_bgCard.withValues(alpha: 0.88),
|
|
2421
|
-
_bgSecondary.withValues(alpha: 0.86),
|
|
2422
|
-
],
|
|
2423
|
-
stops: const <double>[0, 0.22, 1],
|
|
2424
|
-
begin: const Alignment(-1, -1),
|
|
2425
|
-
end: const Alignment(1, 1),
|
|
2426
|
-
),
|
|
2427
|
-
boxShadow: <BoxShadow>[
|
|
2428
|
-
BoxShadow(
|
|
2429
|
-
color: Colors.black.withValues(alpha: 0.22),
|
|
2430
|
-
blurRadius: 34,
|
|
2431
|
-
offset: const Offset(0, 18),
|
|
2432
|
-
),
|
|
2433
|
-
BoxShadow(
|
|
2434
|
-
color: _accent.withValues(alpha: 0.1),
|
|
2435
|
-
blurRadius: 24,
|
|
2436
|
-
offset: const Offset(0, 8),
|
|
2437
|
-
),
|
|
2438
|
-
],
|
|
2439
|
-
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 16),
|
|
2440
|
-
child: compact
|
|
2441
|
-
? Column(
|
|
2442
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
2443
|
-
children: <Widget>[
|
|
2444
|
-
Row(
|
|
2445
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
2446
|
-
children: <Widget>[
|
|
2447
|
-
Container(
|
|
2448
|
-
padding: const EdgeInsets.all(10),
|
|
2449
|
-
decoration: BoxDecoration(
|
|
2450
|
-
color: _accent.withValues(alpha: 0.16),
|
|
2451
|
-
shape: BoxShape.circle,
|
|
2452
|
-
border: Border.all(
|
|
2453
|
-
color: _accent.withValues(alpha: 0.24),
|
|
2454
|
-
),
|
|
2455
|
-
),
|
|
2456
|
-
child: Icon(
|
|
2457
|
-
Icons.cookie_outlined,
|
|
2458
|
-
color: _accent,
|
|
2459
|
-
size: 22,
|
|
2460
|
-
),
|
|
2461
|
-
),
|
|
2462
|
-
const SizedBox(width: 12),
|
|
2463
|
-
Expanded(
|
|
2464
|
-
child: Column(
|
|
2465
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
2466
|
-
children: <Widget>[
|
|
2467
|
-
Text(
|
|
2468
|
-
'Analytics cookies',
|
|
2469
|
-
style: TextStyle(
|
|
2470
|
-
color: _textPrimary,
|
|
2471
|
-
fontSize: 15,
|
|
2472
|
-
fontWeight: FontWeight.w700,
|
|
2473
|
-
),
|
|
2474
|
-
),
|
|
2475
|
-
const SizedBox(height: 6),
|
|
2476
|
-
body,
|
|
2477
|
-
],
|
|
2478
|
-
),
|
|
2479
|
-
),
|
|
2480
|
-
],
|
|
2481
|
-
),
|
|
2482
|
-
const SizedBox(height: 14),
|
|
2483
|
-
Row(
|
|
2484
|
-
children: <Widget>[
|
|
2485
|
-
Expanded(
|
|
2486
|
-
child: OutlinedButton(
|
|
2487
|
-
onPressed: controller.declineAnalyticsConsent,
|
|
2488
|
-
child: const Text('Decline'),
|
|
2489
|
-
),
|
|
2490
|
-
),
|
|
2491
|
-
const SizedBox(width: 10),
|
|
2492
|
-
Expanded(
|
|
2493
|
-
child: FilledButton(
|
|
2494
|
-
onPressed: controller.acceptAnalyticsConsent,
|
|
2495
|
-
child: const Text('Allow analytics'),
|
|
2496
|
-
),
|
|
2497
|
-
),
|
|
2498
|
-
],
|
|
2499
|
-
),
|
|
2500
|
-
],
|
|
2501
|
-
)
|
|
2502
|
-
: Row(
|
|
2503
|
-
crossAxisAlignment: CrossAxisAlignment.center,
|
|
2504
|
-
children: <Widget>[
|
|
2505
|
-
Container(
|
|
2506
|
-
padding: const EdgeInsets.all(10),
|
|
2507
|
-
decoration: BoxDecoration(
|
|
2508
|
-
color: _accent.withValues(alpha: 0.16),
|
|
2509
|
-
shape: BoxShape.circle,
|
|
2510
|
-
border: Border.all(
|
|
2511
|
-
color: _accent.withValues(alpha: 0.24),
|
|
2512
|
-
),
|
|
2513
|
-
),
|
|
2514
|
-
child: Icon(
|
|
2515
|
-
Icons.cookie_outlined,
|
|
2516
|
-
color: _accent,
|
|
2517
|
-
size: 22,
|
|
2518
|
-
),
|
|
2519
|
-
),
|
|
2520
|
-
const SizedBox(width: 14),
|
|
2521
|
-
Expanded(
|
|
2522
|
-
child: Column(
|
|
2523
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
2524
|
-
children: <Widget>[
|
|
2525
|
-
Text(
|
|
2526
|
-
'Analytics cookies',
|
|
2527
|
-
style: TextStyle(
|
|
2528
|
-
color: _textPrimary,
|
|
2529
|
-
fontSize: 15,
|
|
2530
|
-
fontWeight: FontWeight.w700,
|
|
2531
|
-
),
|
|
2532
|
-
),
|
|
2533
|
-
const SizedBox(height: 6),
|
|
2534
|
-
body,
|
|
2535
|
-
],
|
|
2536
|
-
),
|
|
2537
|
-
),
|
|
2538
|
-
const SizedBox(width: 16),
|
|
2539
|
-
OutlinedButton(
|
|
2540
|
-
onPressed: controller.declineAnalyticsConsent,
|
|
2541
|
-
child: const Text('Decline'),
|
|
2542
|
-
),
|
|
2543
|
-
const SizedBox(width: 10),
|
|
2544
|
-
FilledButton(
|
|
2545
|
-
onPressed: controller.acceptAnalyticsConsent,
|
|
2546
|
-
child: const Text('Allow analytics'),
|
|
2547
|
-
),
|
|
2548
|
-
],
|
|
2549
|
-
),
|
|
2550
|
-
),
|
|
2551
|
-
);
|
|
2552
|
-
},
|
|
2553
|
-
);
|
|
2554
|
-
}
|
|
2555
|
-
}
|
|
2556
|
-
|
|
2557
2396
|
class _DesktopCloseDecision {
|
|
2558
2397
|
const _DesktopCloseDecision({
|
|
2559
2398
|
required this.keepRunning,
|
|
@@ -1055,6 +1055,14 @@ class BackendClient {
|
|
|
1055
1055
|
return getList(baseUrl, '/api/skills');
|
|
1056
1056
|
}
|
|
1057
1057
|
|
|
1058
|
+
Future<List<Map<String, dynamic>>> fetchSkillMetrics(String baseUrl) {
|
|
1059
|
+
return getList(baseUrl, '/api/skills/metrics/summary');
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
Future<List<Map<String, dynamic>>> fetchSkillAudit(String baseUrl) {
|
|
1063
|
+
return getList(baseUrl, '/api/skills/audit/summary');
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1058
1066
|
Future<List<Map<String, dynamic>>> fetchSkillStore(String baseUrl) async {
|
|
1059
1067
|
return getList(baseUrl, '/api/store');
|
|
1060
1068
|
}
|
|
@@ -12,7 +12,6 @@ import file_picker
|
|
|
12
12
|
import flutter_secure_storage_macos
|
|
13
13
|
import geolocator_apple
|
|
14
14
|
import hotkey_manager_macos
|
|
15
|
-
import mixpanel_flutter
|
|
16
15
|
import mobile_scanner
|
|
17
16
|
import package_info_plus
|
|
18
17
|
import path_provider_foundation
|
|
@@ -33,7 +32,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|
|
33
32
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
|
34
33
|
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
|
35
34
|
HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin"))
|
|
36
|
-
MixpanelFlutterPlugin.register(with: registry.registrar(forPlugin: "MixpanelFlutterPlugin"))
|
|
37
35
|
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
|
38
36
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
|
39
37
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
package/flutter_app/pubspec.lock
CHANGED
|
@@ -598,14 +598,6 @@ packages:
|
|
|
598
598
|
url: "https://pub.dev"
|
|
599
599
|
source: hosted
|
|
600
600
|
version: "1.18.0"
|
|
601
|
-
mixpanel_flutter:
|
|
602
|
-
dependency: "direct main"
|
|
603
|
-
description:
|
|
604
|
-
name: mixpanel_flutter
|
|
605
|
-
sha256: "6962b8ce61eb6202d6545bee648e21a7bc9f7e804fa293912e9673329f499ece"
|
|
606
|
-
url: "https://pub.dev"
|
|
607
|
-
source: hosted
|
|
608
|
-
version: "2.7.0"
|
|
609
601
|
mobile_scanner:
|
|
610
602
|
dependency: "direct main"
|
|
611
603
|
description:
|
package/flutter_app/pubspec.yaml
CHANGED
package/package.json
CHANGED
package/server/admin/admin.css
CHANGED
|
@@ -185,6 +185,29 @@ button:disabled,
|
|
|
185
185
|
border: 1px solid rgba(222, 138, 120, 0.20);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
.range-btn {
|
|
189
|
+
padding: 5px 12px;
|
|
190
|
+
font-size: 12px;
|
|
191
|
+
font-weight: 600;
|
|
192
|
+
border: 1px solid var(--border);
|
|
193
|
+
border-radius: 6px;
|
|
194
|
+
background: transparent;
|
|
195
|
+
color: var(--text-muted);
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.range-btn:hover {
|
|
201
|
+
background: rgba(224, 240, 224, 0.07);
|
|
202
|
+
color: var(--text);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.range-btn.active {
|
|
206
|
+
background: var(--accent-muted);
|
|
207
|
+
color: var(--accent);
|
|
208
|
+
border-color: var(--accent);
|
|
209
|
+
}
|
|
210
|
+
|
|
188
211
|
.btn-danger:hover:not(:disabled) {
|
|
189
212
|
background: rgba(222, 138, 120, 0.20);
|
|
190
213
|
}
|
package/server/admin/admin.js
CHANGED
|
@@ -355,11 +355,11 @@ async function loadModels() {
|
|
|
355
355
|
try {
|
|
356
356
|
const data = await api('/admin/api/models').then((r) => r.json());
|
|
357
357
|
const models = data.models || [];
|
|
358
|
-
const
|
|
358
|
+
const disabledSet = new Set(data.disabledModels || []);
|
|
359
359
|
|
|
360
|
-
if (!models.length) { el.innerHTML = '<div class="empty">No models found
|
|
360
|
+
if (!models.length) { el.innerHTML = '<div class="empty">No models found — configure providers first.</div>'; return; }
|
|
361
361
|
|
|
362
|
-
//
|
|
362
|
+
// Sort: provider alpha, then price low→high within provider
|
|
363
363
|
models.sort((a, b) => {
|
|
364
364
|
if (a.provider !== b.provider) return a.provider.localeCompare(b.provider);
|
|
365
365
|
const ac = a.inputCostPerM ?? Infinity;
|
|
@@ -367,12 +367,12 @@ async function loadModels() {
|
|
|
367
367
|
return ac - bc;
|
|
368
368
|
});
|
|
369
369
|
|
|
370
|
-
const enabledCount =
|
|
370
|
+
const enabledCount = models.length - disabledSet.size;
|
|
371
371
|
|
|
372
372
|
let html = `
|
|
373
373
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:14px;flex-wrap:wrap;">
|
|
374
|
-
<button class="btn btn-ghost" style="padding:5px 12px;font-size:12px;" onclick="toggleAllModels(true)">
|
|
375
|
-
<button class="btn btn-ghost" style="padding:5px 12px;font-size:12px;" onclick="toggleAllModels(false)">
|
|
374
|
+
<button class="btn btn-ghost" style="padding:5px 12px;font-size:12px;" onclick="toggleAllModels(true)">Enable All</button>
|
|
375
|
+
<button class="btn btn-ghost" style="padding:5px 12px;font-size:12px;" onclick="toggleAllModels(false)">Disable All</button>
|
|
376
376
|
<span style="margin-left:4px;font-size:12px;color:var(--text-muted);" id="model-count-label">${enabledCount} of ${models.length} enabled</span>
|
|
377
377
|
</div>
|
|
378
378
|
<table class="users-table">
|
|
@@ -387,15 +387,14 @@ async function loadModels() {
|
|
|
387
387
|
<tbody>`;
|
|
388
388
|
|
|
389
389
|
for (const m of models) {
|
|
390
|
-
const
|
|
391
|
-
const rowOpacity = isChecked ? '1' : '0.45';
|
|
390
|
+
const isEnabled = !disabledSet.has(m.id);
|
|
392
391
|
const priceStr = fmtModelPrice(m.inputCostPerM);
|
|
393
392
|
const tierCls = priceTierClass(m.priceTier);
|
|
394
393
|
const icon = purposeIcon(m.purpose);
|
|
395
394
|
html += `
|
|
396
|
-
<tr style="opacity:${
|
|
395
|
+
<tr style="opacity:${isEnabled ? '1' : '0.45'}">
|
|
397
396
|
<td style="text-align:center;">
|
|
398
|
-
<input type="checkbox" class="model-cb" value="${esc(m.id)}" ${
|
|
397
|
+
<input type="checkbox" class="model-cb" value="${esc(m.id)}" ${isEnabled ? 'checked' : ''}
|
|
399
398
|
onchange="onModelToggle(this)">
|
|
400
399
|
</td>
|
|
401
400
|
<td>
|
|
@@ -403,7 +402,7 @@ async function loadModels() {
|
|
|
403
402
|
<div style="font-size:11px;color:var(--text-muted);font-family:var(--font-mono);margin-top:2px;">${esc(m.id)}</div>
|
|
404
403
|
</td>
|
|
405
404
|
<td style="font-size:13px;text-transform:capitalize;">${esc(m.provider)}</td>
|
|
406
|
-
<td><span class="badge badge-idle"
|
|
405
|
+
<td><span class="badge badge-idle">${icon} ${esc(m.purpose)}</span></td>
|
|
407
406
|
<td><span class="badge ${tierCls}">${esc(m.priceTier ?? '?')}</span></td>
|
|
408
407
|
<td style="text-align:right;font-family:var(--font-mono);font-size:13px;font-weight:600;color:var(--text);">${priceStr}</td>
|
|
409
408
|
</tr>
|
|
@@ -442,20 +441,19 @@ function toggleAllModels(enable) {
|
|
|
442
441
|
async function saveEnabledModels(btn) {
|
|
443
442
|
const cbs = document.querySelectorAll('.model-cb');
|
|
444
443
|
if (!cbs.length) return;
|
|
445
|
-
|
|
446
|
-
//
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
444
|
+
|
|
445
|
+
// Persist only the disabled (unchecked) models
|
|
446
|
+
const disabledModels = Array.from(cbs).filter(cb => !cb.checked).map(cb => cb.value);
|
|
447
|
+
|
|
450
448
|
btn.disabled = true;
|
|
451
449
|
const original = btn.textContent;
|
|
452
450
|
btn.textContent = 'Saving…';
|
|
453
|
-
|
|
451
|
+
|
|
454
452
|
try {
|
|
455
|
-
const res = await api('/admin/api/models/
|
|
453
|
+
const res = await api('/admin/api/models/config', {
|
|
456
454
|
method: 'PUT',
|
|
457
455
|
headers: { 'Content-Type': 'application/json' },
|
|
458
|
-
body: JSON.stringify({
|
|
456
|
+
body: JSON.stringify({ disabledModels }),
|
|
459
457
|
});
|
|
460
458
|
if (!res.ok) {
|
|
461
459
|
const body = await res.json().catch(() => ({}));
|