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
|
@@ -3778,7 +3778,8 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
3778
3778
|
_desktopFloatingToolbarPopupRequested = false;
|
|
3779
3779
|
unawaited(
|
|
3780
3780
|
_analytics.trackRecordingStopped(
|
|
3781
|
-
kind:
|
|
3781
|
+
kind:
|
|
3782
|
+
recordingRuntime.supportsBackgroundMic &&
|
|
3782
3783
|
!recordingRuntime.supportsScreenAndMic
|
|
3783
3784
|
? 'background'
|
|
3784
3785
|
: 'desktop',
|
|
@@ -5571,9 +5572,18 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
5571
5572
|
return;
|
|
5572
5573
|
}
|
|
5573
5574
|
_pendingChatDraft = normalized;
|
|
5574
|
-
|
|
5575
|
+
if (!_isMobilePlatform) {
|
|
5576
|
+
setSelectedSection(AppSection.chat);
|
|
5577
|
+
} else {
|
|
5578
|
+
notifyListeners();
|
|
5579
|
+
}
|
|
5575
5580
|
}
|
|
5576
5581
|
|
|
5582
|
+
bool get _isMobilePlatform =>
|
|
5583
|
+
!kIsWeb &&
|
|
5584
|
+
(defaultTargetPlatform == TargetPlatform.android ||
|
|
5585
|
+
defaultTargetPlatform == TargetPlatform.iOS);
|
|
5586
|
+
|
|
5577
5587
|
String? takePendingChatDraft() {
|
|
5578
5588
|
final draft = _pendingChatDraft;
|
|
5579
5589
|
_pendingChatDraft = null;
|
|
@@ -5952,7 +5962,7 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
5952
5962
|
settings['headless_browser'] != 'false';
|
|
5953
5963
|
|
|
5954
5964
|
String get browserBackend =>
|
|
5955
|
-
settings['browser_backend']?.toString().trim().toLowerCase() ?? '
|
|
5965
|
+
settings['browser_backend']?.toString().trim().toLowerCase() ?? 'vm';
|
|
5956
5966
|
|
|
5957
5967
|
String get cloudBrowserBackend {
|
|
5958
5968
|
final browser = browserBackend;
|
|
@@ -5968,11 +5978,10 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
5968
5978
|
profile == 'secure-vm') {
|
|
5969
5979
|
return 'vm';
|
|
5970
5980
|
}
|
|
5971
|
-
if (browser == '
|
|
5972
|
-
|
|
5973
|
-
}
|
|
5981
|
+
if (browser == 'extension') return 'vm';
|
|
5982
|
+
if (browser == 'vm') return 'vm';
|
|
5974
5983
|
if (runtime == 'vm') return 'vm';
|
|
5975
|
-
return '
|
|
5984
|
+
return 'vm';
|
|
5976
5985
|
}
|
|
5977
5986
|
|
|
5978
5987
|
bool get browserExtensionConnected =>
|
|
@@ -6157,7 +6166,19 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
6157
6166
|
|
|
6158
6167
|
List<ChatEntry> get visibleChatMessages {
|
|
6159
6168
|
final entries = <ChatEntry>[...chatMessages];
|
|
6160
|
-
if (streamingAssistant.trim().
|
|
6169
|
+
if (activeRun != null && streamingAssistant.trim().isEmpty) {
|
|
6170
|
+
entries.add(
|
|
6171
|
+
ChatEntry(
|
|
6172
|
+
id: '',
|
|
6173
|
+
role: 'assistant',
|
|
6174
|
+
content: '',
|
|
6175
|
+
platform: 'live',
|
|
6176
|
+
createdAt: DateTime.now(),
|
|
6177
|
+
transient: true,
|
|
6178
|
+
typing: true,
|
|
6179
|
+
),
|
|
6180
|
+
);
|
|
6181
|
+
} else if (streamingAssistant.trim().isNotEmpty) {
|
|
6161
6182
|
entries.add(
|
|
6162
6183
|
ChatEntry(
|
|
6163
6184
|
id: '',
|
|
@@ -378,9 +378,7 @@ class _DevicesPanelState extends State<DevicesPanel> {
|
|
|
378
378
|
final prefersExtension = controller.browserBackend == 'extension';
|
|
379
379
|
final extensionConnected = controller.browserExtensionConnected;
|
|
380
380
|
final usingExtension = prefersExtension && extensionConnected;
|
|
381
|
-
final browserFallbackLabel =
|
|
382
|
-
? 'cloud VM'
|
|
383
|
-
: 'local host';
|
|
381
|
+
final browserFallbackLabel = 'cloud browser runtime';
|
|
384
382
|
final browserPageInfo = browserStatus['pageInfo'] is Map<dynamic, dynamic>
|
|
385
383
|
? Map<String, dynamic>.from(browserStatus['pageInfo'] as Map)
|
|
386
384
|
: const <String, dynamic>{};
|
|
@@ -682,9 +680,9 @@ class _DeviceSurfaceHeader extends StatelessWidget {
|
|
|
682
680
|
: 'Desktop Companion',
|
|
683
681
|
};
|
|
684
682
|
final subtitle = switch (surface) {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
? 'No extension device is active. Using the $browserFallbackLabel
|
|
683
|
+
_DeviceSurface.browser =>
|
|
684
|
+
browserExtensionPreferred && !browserExtensionActive
|
|
685
|
+
? 'No extension device is active. Using the $browserFallbackLabel.'
|
|
688
686
|
: (browserPageInfo['url']?.toString() ??
|
|
689
687
|
'Ready for navigation'),
|
|
690
688
|
_DeviceSurface.android =>
|
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
part of 'main.dart';
|
|
2
2
|
|
|
3
3
|
class IntegrationsPanel extends StatelessWidget {
|
|
4
|
-
const IntegrationsPanel({
|
|
4
|
+
const IntegrationsPanel({
|
|
5
|
+
super.key,
|
|
6
|
+
required this.controller,
|
|
7
|
+
this.embedded = false,
|
|
8
|
+
});
|
|
5
9
|
|
|
6
10
|
final NeoAgentController controller;
|
|
11
|
+
final bool embedded;
|
|
7
12
|
|
|
8
13
|
@override
|
|
9
14
|
Widget build(BuildContext context) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
children: <Widget>[
|
|
15
|
+
final body = Column(
|
|
16
|
+
children: <Widget>[
|
|
17
|
+
if (!embedded) ...<Widget>[
|
|
14
18
|
_PageTitle(
|
|
15
19
|
title: 'Integrations',
|
|
16
20
|
subtitle:
|
|
17
21
|
'Connect and manage official integrations separately from reusable skills.',
|
|
18
22
|
),
|
|
19
23
|
const SizedBox(height: 12),
|
|
20
|
-
Expanded(child: OfficialIntegrationsTab(controller: controller)),
|
|
21
24
|
],
|
|
22
|
-
|
|
25
|
+
Expanded(child: OfficialIntegrationsTab(controller: controller)),
|
|
26
|
+
],
|
|
23
27
|
);
|
|
28
|
+
if (embedded) {
|
|
29
|
+
return body;
|
|
30
|
+
}
|
|
31
|
+
return Padding(padding: _pagePadding(context), child: body);
|
|
24
32
|
}
|
|
25
33
|
}
|
|
26
34
|
|
|
@@ -832,7 +832,7 @@ class MessagingAccessCapabilities {
|
|
|
832
832
|
});
|
|
833
833
|
|
|
834
834
|
factory MessagingAccessCapabilities.fromJson(Map<String, dynamic> json) {
|
|
835
|
-
List<String>
|
|
835
|
+
List<String> stringList(dynamic value) {
|
|
836
836
|
if (value is! List) return const <String>[];
|
|
837
837
|
return value
|
|
838
838
|
.map((item) => item.toString())
|
|
@@ -845,9 +845,9 @@ class MessagingAccessCapabilities {
|
|
|
845
845
|
supportsSharedPolicy: json['supportsSharedPolicy'] != false,
|
|
846
846
|
supportsMentionGate: json['supportsMentionGate'] == true,
|
|
847
847
|
supportsDiscovery: json['supportsDiscovery'] == true,
|
|
848
|
-
directRuleScopes:
|
|
849
|
-
sharedSpaceRuleScopes:
|
|
850
|
-
sharedActorRuleScopes:
|
|
848
|
+
directRuleScopes: stringList(json['directRuleScopes']),
|
|
849
|
+
sharedSpaceRuleScopes: stringList(json['sharedSpaceRuleScopes']),
|
|
850
|
+
sharedActorRuleScopes: stringList(json['sharedActorRuleScopes']),
|
|
851
851
|
manualEntryHint: json['manualEntryHint']?.toString() ?? '',
|
|
852
852
|
);
|
|
853
853
|
}
|
|
@@ -932,7 +932,7 @@ class MessagingAccessCatalog {
|
|
|
932
932
|
String platform,
|
|
933
933
|
Map<String, dynamic> json,
|
|
934
934
|
) {
|
|
935
|
-
List<MessagingAccessTarget>
|
|
935
|
+
List<MessagingAccessTarget> parseTargets(dynamic raw) {
|
|
936
936
|
if (raw is! List) return const <MessagingAccessTarget>[];
|
|
937
937
|
return raw
|
|
938
938
|
.whereType<Map>()
|
|
@@ -950,8 +950,8 @@ class MessagingAccessCatalog {
|
|
|
950
950
|
capabilities: MessagingAccessCapabilities.fromJson(
|
|
951
951
|
_jsonMap(json['capabilities']),
|
|
952
952
|
),
|
|
953
|
-
discoveredTargets:
|
|
954
|
-
suggestedTargets:
|
|
953
|
+
discoveredTargets: parseTargets(json['discoveredTargets']),
|
|
954
|
+
suggestedTargets: parseTargets(json['suggestedTargets']),
|
|
955
955
|
summary: json['summary']?.toString() ?? 'Access policy',
|
|
956
956
|
);
|
|
957
957
|
}
|
|
@@ -1434,6 +1434,7 @@ class RunDetailSnapshot {
|
|
|
1434
1434
|
const RunDetailSnapshot({
|
|
1435
1435
|
required this.run,
|
|
1436
1436
|
required this.steps,
|
|
1437
|
+
required this.events,
|
|
1437
1438
|
required this.response,
|
|
1438
1439
|
});
|
|
1439
1440
|
|
|
@@ -1444,12 +1445,17 @@ class RunDetailSnapshot {
|
|
|
1444
1445
|
json['steps'],
|
|
1445
1446
|
fallbackToMapValues: true,
|
|
1446
1447
|
).map(RunStepItem.fromJson).toList(),
|
|
1448
|
+
events: _jsonMapList(
|
|
1449
|
+
json['events'],
|
|
1450
|
+
fallbackToMapValues: true,
|
|
1451
|
+
).map(RunEventItem.fromJson).toList(),
|
|
1447
1452
|
response: json['response']?.toString() ?? '',
|
|
1448
1453
|
);
|
|
1449
1454
|
}
|
|
1450
1455
|
|
|
1451
1456
|
final RunSummary run;
|
|
1452
1457
|
final List<RunStepItem> steps;
|
|
1458
|
+
final List<RunEventItem> events;
|
|
1453
1459
|
final String response;
|
|
1454
1460
|
|
|
1455
1461
|
int get completedTools => steps
|
|
@@ -1469,6 +1475,146 @@ class RunDetailSnapshot {
|
|
|
1469
1475
|
steps.where((step) => step.isPlanningRelated).length;
|
|
1470
1476
|
}
|
|
1471
1477
|
|
|
1478
|
+
class ArtifactContractItem {
|
|
1479
|
+
const ArtifactContractItem({
|
|
1480
|
+
required this.kind,
|
|
1481
|
+
required this.path,
|
|
1482
|
+
required this.uri,
|
|
1483
|
+
required this.label,
|
|
1484
|
+
required this.mimeType,
|
|
1485
|
+
required this.size,
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
factory ArtifactContractItem.fromJson(Map<dynamic, dynamic> json) {
|
|
1489
|
+
return ArtifactContractItem(
|
|
1490
|
+
kind: json['kind']?.toString() ?? 'artifact',
|
|
1491
|
+
path: json['path']?.toString() ?? '',
|
|
1492
|
+
uri: json['uri']?.toString() ?? json['url']?.toString() ?? '',
|
|
1493
|
+
label: json['label']?.toString() ?? '',
|
|
1494
|
+
mimeType:
|
|
1495
|
+
json['mimeType']?.toString() ?? json['mime_type']?.toString() ?? '',
|
|
1496
|
+
size: _asInt(json['size'] ?? json['byte_size']),
|
|
1497
|
+
);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
final String kind;
|
|
1501
|
+
final String path;
|
|
1502
|
+
final String uri;
|
|
1503
|
+
final String label;
|
|
1504
|
+
final String mimeType;
|
|
1505
|
+
final int size;
|
|
1506
|
+
|
|
1507
|
+
String get displayLabel => label.ifEmpty(path.ifEmpty(uri.ifEmpty(kind)));
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
class RunEventItem {
|
|
1511
|
+
const RunEventItem({
|
|
1512
|
+
required this.id,
|
|
1513
|
+
required this.eventType,
|
|
1514
|
+
required this.sequenceIndex,
|
|
1515
|
+
required this.requestId,
|
|
1516
|
+
required this.stepId,
|
|
1517
|
+
required this.payload,
|
|
1518
|
+
required this.createdAt,
|
|
1519
|
+
});
|
|
1520
|
+
|
|
1521
|
+
factory RunEventItem.fromJson(Map<dynamic, dynamic> json) {
|
|
1522
|
+
return RunEventItem(
|
|
1523
|
+
id: _asInt(json['id']),
|
|
1524
|
+
eventType:
|
|
1525
|
+
json['eventType']?.toString().ifEmpty(
|
|
1526
|
+
json['event_type']?.toString() ?? 'event',
|
|
1527
|
+
) ??
|
|
1528
|
+
'event',
|
|
1529
|
+
sequenceIndex: _asInt(json['sequenceIndex'] ?? json['sequence_index']),
|
|
1530
|
+
requestId:
|
|
1531
|
+
json['requestId']?.toString() ?? json['request_id']?.toString(),
|
|
1532
|
+
stepId: json['stepId']?.toString() ?? json['step_id']?.toString(),
|
|
1533
|
+
payload: json['payload'] is Map
|
|
1534
|
+
? Map<String, dynamic>.from(json['payload'] as Map)
|
|
1535
|
+
: (json['payload_json'] is Map
|
|
1536
|
+
? Map<String, dynamic>.from(json['payload_json'] as Map)
|
|
1537
|
+
: const <String, dynamic>{}),
|
|
1538
|
+
createdAt: _parseOptionalTimestamp(
|
|
1539
|
+
json['createdAt']?.toString() ?? json['created_at']?.toString(),
|
|
1540
|
+
),
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
final int id;
|
|
1545
|
+
final String eventType;
|
|
1546
|
+
final int sequenceIndex;
|
|
1547
|
+
final String? requestId;
|
|
1548
|
+
final String? stepId;
|
|
1549
|
+
final Map<String, dynamic> payload;
|
|
1550
|
+
final DateTime? createdAt;
|
|
1551
|
+
|
|
1552
|
+
String get title {
|
|
1553
|
+
switch (eventType) {
|
|
1554
|
+
case 'deliverable_workflow_selected':
|
|
1555
|
+
return 'Deliverable selected';
|
|
1556
|
+
case 'deliverable_execution_started':
|
|
1557
|
+
return 'Deliverable execution started';
|
|
1558
|
+
case 'deliverable_artifact_produced':
|
|
1559
|
+
return 'Deliverable artifact produced';
|
|
1560
|
+
case 'deliverable_validation_started':
|
|
1561
|
+
return 'Deliverable validation started';
|
|
1562
|
+
case 'deliverable_validation_failed':
|
|
1563
|
+
return 'Deliverable validation failed';
|
|
1564
|
+
case 'deliverable_completed':
|
|
1565
|
+
return 'Deliverable completed';
|
|
1566
|
+
case 'run_started':
|
|
1567
|
+
return 'Run started';
|
|
1568
|
+
case 'memory_injected':
|
|
1569
|
+
return 'Memory injected';
|
|
1570
|
+
case 'model_turn_started':
|
|
1571
|
+
return 'Model turn started';
|
|
1572
|
+
case 'model_turn_completed':
|
|
1573
|
+
return 'Model turn completed';
|
|
1574
|
+
case 'tool_started':
|
|
1575
|
+
return 'Tool started';
|
|
1576
|
+
case 'tool_completed':
|
|
1577
|
+
return 'Tool completed';
|
|
1578
|
+
case 'tool_failed':
|
|
1579
|
+
return 'Tool failed';
|
|
1580
|
+
case 'run_completed':
|
|
1581
|
+
return 'Run completed';
|
|
1582
|
+
case 'run_failed':
|
|
1583
|
+
return 'Run failed';
|
|
1584
|
+
case 'run_stopped':
|
|
1585
|
+
return 'Run stopped';
|
|
1586
|
+
default:
|
|
1587
|
+
return _titleCase(eventType.replaceAll('_', ' '));
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
String get detail {
|
|
1592
|
+
final toolName = payload['toolName']?.toString() ?? '';
|
|
1593
|
+
if (toolName.trim().isNotEmpty) return toolName;
|
|
1594
|
+
final preview =
|
|
1595
|
+
payload['contentPreview']?.toString() ??
|
|
1596
|
+
payload['recallPreview']?.toString() ??
|
|
1597
|
+
'';
|
|
1598
|
+
if (preview.trim().isNotEmpty) return preview;
|
|
1599
|
+
final error = payload['error']?.toString() ?? '';
|
|
1600
|
+
if (error.trim().isNotEmpty) return error;
|
|
1601
|
+
final artifactLabel = payload['artifact'] is Map
|
|
1602
|
+
? (payload['artifact']['label']?.toString() ??
|
|
1603
|
+
payload['artifact']['path']?.toString() ??
|
|
1604
|
+
payload['artifact']['uri']?.toString() ??
|
|
1605
|
+
'')
|
|
1606
|
+
: '';
|
|
1607
|
+
if (artifactLabel.trim().isNotEmpty) return artifactLabel;
|
|
1608
|
+
final titleValue = payload['title']?.toString() ?? '';
|
|
1609
|
+
return titleValue;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
String get createdAtLabel =>
|
|
1613
|
+
createdAt == null ? '' : _formatTimestamp(createdAt!);
|
|
1614
|
+
|
|
1615
|
+
bool get isFailure => eventType == 'tool_failed' || eventType == 'run_failed';
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1472
1618
|
class RunStepItem {
|
|
1473
1619
|
const RunStepItem({
|
|
1474
1620
|
required this.id,
|
|
@@ -1650,6 +1796,17 @@ dynamic _decodeMaybeJson(dynamic value) {
|
|
|
1650
1796
|
return value;
|
|
1651
1797
|
}
|
|
1652
1798
|
|
|
1799
|
+
Map<String, dynamic> _decodeJsonMap(
|
|
1800
|
+
String? value, {
|
|
1801
|
+
Map<String, dynamic> fallback = const <String, dynamic>{},
|
|
1802
|
+
}) {
|
|
1803
|
+
final decoded = _decodeMaybeJson(value);
|
|
1804
|
+
if (decoded is Map) {
|
|
1805
|
+
return Map<String, dynamic>.from(decoded);
|
|
1806
|
+
}
|
|
1807
|
+
return fallback;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1653
1810
|
class ChatEntry {
|
|
1654
1811
|
const ChatEntry({
|
|
1655
1812
|
required this.id,
|
|
@@ -1662,6 +1819,7 @@ class ChatEntry {
|
|
|
1662
1819
|
this.metadata = const <String, dynamic>{},
|
|
1663
1820
|
this.toolCalls = const <Map<String, dynamic>>[],
|
|
1664
1821
|
this.transient = false,
|
|
1822
|
+
this.typing = false,
|
|
1665
1823
|
});
|
|
1666
1824
|
|
|
1667
1825
|
factory ChatEntry.fromJson(Map<dynamic, dynamic> json) {
|
|
@@ -1691,6 +1849,7 @@ class ChatEntry {
|
|
|
1691
1849
|
final List<Map<String, dynamic>> toolCalls;
|
|
1692
1850
|
final DateTime createdAt;
|
|
1693
1851
|
final bool transient;
|
|
1852
|
+
final bool typing;
|
|
1694
1853
|
|
|
1695
1854
|
String get createdAtLabel => _formatTimestamp(createdAt);
|
|
1696
1855
|
|
|
@@ -1964,9 +2123,16 @@ class RunSummary {
|
|
|
1964
2123
|
required this.createdAt,
|
|
1965
2124
|
this.completedAt,
|
|
1966
2125
|
this.error = '',
|
|
2126
|
+
this.metadata = const <String, dynamic>{},
|
|
1967
2127
|
});
|
|
1968
2128
|
|
|
1969
2129
|
factory RunSummary.fromJson(Map<dynamic, dynamic> json) {
|
|
2130
|
+
final metadata = _decodeJsonMap(
|
|
2131
|
+
json['metadata_json']?.toString(),
|
|
2132
|
+
fallback: json['metadata'] is Map
|
|
2133
|
+
? Map<String, dynamic>.from(json['metadata'] as Map)
|
|
2134
|
+
: const <String, dynamic>{},
|
|
2135
|
+
);
|
|
1970
2136
|
return RunSummary(
|
|
1971
2137
|
id: json['id']?.toString() ?? '',
|
|
1972
2138
|
title: json['title']?.toString() ?? 'Untitled',
|
|
@@ -1977,6 +2143,7 @@ class RunSummary {
|
|
|
1977
2143
|
createdAt: _parseTimestamp(json['created_at']?.toString()),
|
|
1978
2144
|
completedAt: _parseOptionalTimestamp(json['completed_at']?.toString()),
|
|
1979
2145
|
error: json['error']?.toString() ?? '',
|
|
2146
|
+
metadata: metadata,
|
|
1980
2147
|
);
|
|
1981
2148
|
}
|
|
1982
2149
|
|
|
@@ -1989,6 +2156,24 @@ class RunSummary {
|
|
|
1989
2156
|
final DateTime createdAt;
|
|
1990
2157
|
final DateTime? completedAt;
|
|
1991
2158
|
final String error;
|
|
2159
|
+
final Map<String, dynamic> metadata;
|
|
2160
|
+
|
|
2161
|
+
Map<String, dynamic> get deliverable => metadata['deliverable'] is Map
|
|
2162
|
+
? Map<String, dynamic>.from(metadata['deliverable'] as Map)
|
|
2163
|
+
: const <String, dynamic>{};
|
|
2164
|
+
|
|
2165
|
+
String get deliverableType => deliverable['type']?.toString() ?? '';
|
|
2166
|
+
|
|
2167
|
+
String get deliverableSummary => deliverable['summary']?.toString() ?? '';
|
|
2168
|
+
|
|
2169
|
+
List<ArtifactContractItem> get deliverableArtifacts {
|
|
2170
|
+
final raw = deliverable['artifacts'];
|
|
2171
|
+
if (raw is! List) return const <ArtifactContractItem>[];
|
|
2172
|
+
return raw
|
|
2173
|
+
.whereType<Map>()
|
|
2174
|
+
.map(ArtifactContractItem.fromJson)
|
|
2175
|
+
.toList(growable: false);
|
|
2176
|
+
}
|
|
1992
2177
|
|
|
1993
2178
|
bool get isFailure => status == 'failed' || status == 'error';
|
|
1994
2179
|
|
|
@@ -2162,7 +2347,7 @@ class UpdateStatusSnapshot {
|
|
|
2162
2347
|
deploymentProfile.toLowerCase() == 'prod' ? 'Production' : 'Private';
|
|
2163
2348
|
|
|
2164
2349
|
String get runtimeModeLabel => deploymentProfile.toLowerCase() == 'prod'
|
|
2165
|
-
? '
|
|
2350
|
+
? 'Cloud runtime'
|
|
2166
2351
|
: 'Trusted host runtime';
|
|
2167
2352
|
|
|
2168
2353
|
String get runtimeValidationLabel =>
|
|
@@ -2802,6 +2987,9 @@ class AiWidgetItem {
|
|
|
2802
2987
|
required this.userId,
|
|
2803
2988
|
required this.agentId,
|
|
2804
2989
|
required this.name,
|
|
2990
|
+
required this.widgetKind,
|
|
2991
|
+
required this.systemKey,
|
|
2992
|
+
required this.isSystem,
|
|
2805
2993
|
required this.template,
|
|
2806
2994
|
required this.layoutVariant,
|
|
2807
2995
|
required this.definition,
|
|
@@ -2823,6 +3011,14 @@ class AiWidgetItem {
|
|
|
2823
3011
|
userId: _asInt(json['userId'] ?? json['user_id']),
|
|
2824
3012
|
agentId: json['agentId']?.toString() ?? json['agent_id']?.toString(),
|
|
2825
3013
|
name: json['name']?.toString().ifEmpty('Widget') ?? 'Widget',
|
|
3014
|
+
widgetKind:
|
|
3015
|
+
json['widgetKind']?.toString().ifEmpty(
|
|
3016
|
+
json['widget_kind']?.toString() ?? 'custom',
|
|
3017
|
+
) ??
|
|
3018
|
+
'custom',
|
|
3019
|
+
systemKey:
|
|
3020
|
+
json['systemKey']?.toString() ?? json['system_key']?.toString(),
|
|
3021
|
+
isSystem: json['isSystem'] == true || json['is_system'] == true,
|
|
2826
3022
|
template: json['template']?.toString().ifEmpty('summary') ?? 'summary',
|
|
2827
3023
|
layoutVariant:
|
|
2828
3024
|
json['layoutVariant']?.toString().ifEmpty(
|
|
@@ -2876,6 +3072,9 @@ class AiWidgetItem {
|
|
|
2876
3072
|
final int userId;
|
|
2877
3073
|
final String? agentId;
|
|
2878
3074
|
final String name;
|
|
3075
|
+
final String widgetKind;
|
|
3076
|
+
final String? systemKey;
|
|
3077
|
+
final bool isSystem;
|
|
2879
3078
|
final String template;
|
|
2880
3079
|
final String layoutVariant;
|
|
2881
3080
|
final Map<String, dynamic> definition;
|
|
@@ -32,7 +32,7 @@ enum AppSection {
|
|
|
32
32
|
health,
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
enum SidebarGroup { chat, recordings,
|
|
35
|
+
enum SidebarGroup { chat, recordings, automation, settings }
|
|
36
36
|
|
|
37
37
|
extension SidebarGroupX on SidebarGroup {
|
|
38
38
|
String get label {
|
|
@@ -41,8 +41,6 @@ extension SidebarGroupX on SidebarGroup {
|
|
|
41
41
|
return 'Chat';
|
|
42
42
|
case SidebarGroup.recordings:
|
|
43
43
|
return 'Recordings';
|
|
44
|
-
case SidebarGroup.activity:
|
|
45
|
-
return 'Activity';
|
|
46
44
|
case SidebarGroup.automation:
|
|
47
45
|
return 'Automation';
|
|
48
46
|
case SidebarGroup.settings:
|
|
@@ -56,8 +54,6 @@ extension SidebarGroupX on SidebarGroup {
|
|
|
56
54
|
return Icons.chat_bubble_outline;
|
|
57
55
|
case SidebarGroup.recordings:
|
|
58
56
|
return Icons.fiber_smart_record_outlined;
|
|
59
|
-
case SidebarGroup.activity:
|
|
60
|
-
return Icons.insights_outlined;
|
|
61
57
|
case SidebarGroup.automation:
|
|
62
58
|
return Icons.auto_awesome_outlined;
|
|
63
59
|
case SidebarGroup.settings:
|
|
@@ -80,7 +76,7 @@ extension AppSectionX on AppSection {
|
|
|
80
76
|
case AppSection.messaging:
|
|
81
77
|
return 'Messaging';
|
|
82
78
|
case AppSection.runs:
|
|
83
|
-
return 'Runs';
|
|
79
|
+
return 'Runs & Logs';
|
|
84
80
|
case AppSection.settings:
|
|
85
81
|
return 'Settings';
|
|
86
82
|
case AppSection.accountSettings:
|
|
@@ -92,7 +88,7 @@ extension AppSectionX on AppSection {
|
|
|
92
88
|
case AppSection.agents:
|
|
93
89
|
return 'Agents';
|
|
94
90
|
case AppSection.integrations:
|
|
95
|
-
return '
|
|
91
|
+
return 'Tools';
|
|
96
92
|
case AppSection.memory:
|
|
97
93
|
return 'Memory';
|
|
98
94
|
case AppSection.tasks:
|
|
@@ -119,7 +115,7 @@ extension AppSectionX on AppSection {
|
|
|
119
115
|
case AppSection.messaging:
|
|
120
116
|
return Icons.forum_outlined;
|
|
121
117
|
case AppSection.runs:
|
|
122
|
-
return Icons.
|
|
118
|
+
return Icons.monitor_heart_outlined;
|
|
123
119
|
case AppSection.settings:
|
|
124
120
|
return Icons.tune;
|
|
125
121
|
case AppSection.accountSettings:
|
|
@@ -131,7 +127,7 @@ extension AppSectionX on AppSection {
|
|
|
131
127
|
case AppSection.agents:
|
|
132
128
|
return Icons.smart_toy_outlined;
|
|
133
129
|
case AppSection.integrations:
|
|
134
|
-
return Icons.
|
|
130
|
+
return Icons.handyman_outlined;
|
|
135
131
|
case AppSection.memory:
|
|
136
132
|
return Icons.psychology_outlined;
|
|
137
133
|
case AppSection.tasks:
|
|
@@ -152,9 +148,6 @@ extension AppSectionX on AppSection {
|
|
|
152
148
|
return SidebarGroup.chat;
|
|
153
149
|
case AppSection.recordings:
|
|
154
150
|
return SidebarGroup.recordings;
|
|
155
|
-
case AppSection.runs:
|
|
156
|
-
case AppSection.logs:
|
|
157
|
-
return SidebarGroup.activity;
|
|
158
151
|
case AppSection.devices:
|
|
159
152
|
case AppSection.skills:
|
|
160
153
|
case AppSection.integrations:
|
|
@@ -164,6 +157,8 @@ extension AppSectionX on AppSection {
|
|
|
164
157
|
case AppSection.mcp:
|
|
165
158
|
case AppSection.health:
|
|
166
159
|
return SidebarGroup.automation;
|
|
160
|
+
case AppSection.runs:
|
|
161
|
+
case AppSection.logs:
|
|
167
162
|
case AppSection.settings:
|
|
168
163
|
case AppSection.accountSettings:
|
|
169
164
|
case AppSection.messaging:
|
|
@@ -172,17 +167,40 @@ extension AppSectionX on AppSection {
|
|
|
172
167
|
}
|
|
173
168
|
}
|
|
174
169
|
|
|
170
|
+
AppSection get canonicalSection {
|
|
171
|
+
switch (this) {
|
|
172
|
+
case AppSection.logs:
|
|
173
|
+
return AppSection.runs;
|
|
174
|
+
case AppSection.skills:
|
|
175
|
+
case AppSection.mcp:
|
|
176
|
+
return AppSection.integrations;
|
|
177
|
+
default:
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
AppSection get sidebarSection {
|
|
183
|
+
switch (this) {
|
|
184
|
+
case AppSection.accountSettings:
|
|
185
|
+
return AppSection.settings;
|
|
186
|
+
default:
|
|
187
|
+
return canonicalSection;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
175
191
|
String get navigationTitle {
|
|
176
|
-
final
|
|
177
|
-
|
|
178
|
-
|
|
192
|
+
final effectiveSection = canonicalSection;
|
|
193
|
+
final groupLabel = effectiveSection.group.label;
|
|
194
|
+
if (effectiveSection == AppSection.voiceAssistant) {
|
|
195
|
+
return effectiveSection.label;
|
|
179
196
|
}
|
|
180
|
-
if (group == SidebarGroup.chat ||
|
|
197
|
+
if (effectiveSection.group == SidebarGroup.chat ||
|
|
198
|
+
effectiveSection.group == SidebarGroup.recordings) {
|
|
181
199
|
return groupLabel;
|
|
182
200
|
}
|
|
183
|
-
if (groupLabel == label) {
|
|
201
|
+
if (groupLabel == effectiveSection.label) {
|
|
184
202
|
return groupLabel;
|
|
185
203
|
}
|
|
186
|
-
return '$groupLabel · $label';
|
|
204
|
+
return '$groupLabel · ${effectiveSection.label}';
|
|
187
205
|
}
|
|
188
206
|
}
|