neoagent 3.2.1-beta.0 → 3.2.1-beta.10
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/agent-run-lifecycle.md +10 -0
- package/extensions/chrome-browser/background.mjs +318 -88
- package/extensions/chrome-browser/http.mjs +136 -0
- package/extensions/chrome-browser/protocol.mjs +654 -90
- package/flutter_app/lib/main_chat.dart +118 -739
- package/flutter_app/lib/main_controller.dart +111 -20
- package/flutter_app/lib/main_integrations.dart +607 -8
- package/flutter_app/lib/main_models.dart +3 -0
- package/flutter_app/lib/main_operations.dart +334 -321
- package/flutter_app/lib/main_security.dart +266 -112
- package/flutter_app/lib/main_settings.dart +4 -3
- package/flutter_app/lib/main_shared.dart +14 -11
- package/flutter_app/lib/src/backend_client.dart +78 -0
- package/flutter_app/lib/src/desktop_companion_actions.dart +185 -31
- package/flutter_app/lib/src/desktop_companion_io.dart +319 -86
- package/flutter_app/windows/runner/flutter_window.cpp +143 -32
- package/landing/index.html +3 -1
- package/lib/manager.js +106 -89
- package/lib/schema_migrations.js +145 -13
- package/package.json +30 -15
- package/runtime/paths.js +49 -5
- package/server/db/database.js +4 -4
- package/server/guest-agent.cli.package.json +13 -0
- package/server/guest_agent.js +85 -40
- package/server/http/middleware.js +24 -0
- package/server/http/routes.js +11 -6
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/main.dart.js +73083 -72209
- package/server/routes/admin.js +1 -1
- package/server/routes/agents.js +35 -2
- package/server/routes/android.js +30 -34
- package/server/routes/browser.js +23 -15
- package/server/routes/desktop.js +18 -1
- package/server/routes/integrations.js +107 -1
- package/server/routes/memory.js +1 -0
- package/server/routes/settings.js +16 -5
- package/server/routes/social_reach.js +12 -3
- package/server/routes/social_video.js +4 -0
- package/server/services/agents/manager.js +1 -1
- package/server/services/ai/capabilityHealth.js +62 -96
- package/server/services/ai/compaction.js +7 -2
- package/server/services/ai/history.js +45 -6
- package/server/services/ai/integrated_tools/http_request.js +8 -0
- package/server/services/ai/loop/agent_engine_core.js +496 -166
- package/server/services/ai/loop/blank_recovery.js +5 -4
- package/server/services/ai/loop/callbacks.js +1 -0
- package/server/services/ai/loop/completion_judge.js +121 -5
- package/server/services/ai/loop/conversation_loop.js +620 -340
- package/server/services/ai/loop/lifecycle.js +108 -0
- package/server/services/ai/loop/messaging_delivery.js +129 -57
- package/server/services/ai/loop/model_call_guard.js +91 -0
- package/server/services/ai/loop/model_io.js +48 -56
- package/server/services/ai/loop/progress_classification.js +2 -0
- package/server/services/ai/loop/tool_dispatch.js +28 -8
- package/server/services/ai/loopPolicy.js +48 -21
- package/server/services/ai/messagingFallback.js +17 -17
- package/server/services/ai/model_discovery.js +227 -0
- package/server/services/ai/model_failure_cache.js +108 -0
- package/server/services/ai/model_identity.js +71 -0
- package/server/services/ai/models.js +68 -163
- package/server/services/ai/providerRetry.js +17 -59
- package/server/services/ai/provider_selector.js +166 -0
- package/server/services/ai/providers/anthropic.js +4 -4
- package/server/services/ai/providers/claudeCode.js +21 -33
- package/server/services/ai/providers/githubCopilot.js +41 -20
- package/server/services/ai/providers/google.js +135 -97
- package/server/services/ai/providers/grok.js +6 -5
- package/server/services/ai/providers/grokOauth.js +19 -27
- package/server/services/ai/providers/nvidia.js +12 -7
- package/server/services/ai/providers/ollama.js +114 -86
- package/server/services/ai/providers/ollama_stream.js +142 -0
- package/server/services/ai/providers/openai.js +41 -7
- package/server/services/ai/providers/openaiCodex.js +13 -4
- package/server/services/ai/providers/openrouter.js +31 -9
- package/server/services/ai/providers/provider_error.js +36 -0
- package/server/services/ai/settings.js +26 -2
- package/server/services/ai/systemPrompt.js +19 -12
- package/server/services/ai/taskAnalysis.js +58 -10
- package/server/services/ai/terminal_reply.js +18 -0
- package/server/services/ai/toolEvidence.js +350 -29
- package/server/services/ai/tools.js +190 -111
- package/server/services/android/controller.js +770 -237
- package/server/services/android/process.js +140 -0
- package/server/services/android/sdk_download.js +143 -0
- package/server/services/android/uia.js +6 -5
- package/server/services/artifacts/store.js +24 -0
- package/server/services/browser/controller.js +843 -385
- package/server/services/browser/extension/gateway.js +40 -16
- package/server/services/browser/extension/protocol.js +15 -1
- package/server/services/browser/extension/provider.js +71 -47
- package/server/services/browser/extension/registry.js +155 -34
- package/server/services/cli/executor.js +62 -9
- package/server/services/credentials/bitwarden_cli.js +322 -0
- package/server/services/credentials/broker.js +594 -0
- package/server/services/desktop/gateway.js +41 -4
- package/server/services/desktop/protocol.js +3 -0
- package/server/services/desktop/provider.js +39 -42
- package/server/services/desktop/registry.js +137 -52
- package/server/services/integrations/bitwarden/constants.js +14 -0
- package/server/services/integrations/bitwarden/provider.js +197 -0
- package/server/services/integrations/bitwarden/snapshot.js +65 -0
- package/server/services/integrations/figma/provider.js +78 -12
- package/server/services/integrations/github/common.js +11 -6
- package/server/services/integrations/github/provider.js +52 -53
- package/server/services/integrations/google/provider.js +55 -19
- package/server/services/integrations/home_assistant/network.js +17 -20
- package/server/services/integrations/home_assistant/provider.js +7 -5
- package/server/services/integrations/home_assistant/tools.js +17 -5
- package/server/services/integrations/http.js +51 -0
- package/server/services/integrations/manager.js +159 -53
- package/server/services/integrations/microsoft/provider.js +80 -13
- package/server/services/integrations/neoarchive/provider.js +55 -29
- package/server/services/integrations/neorecall/client.js +17 -10
- package/server/services/integrations/neorecall/provider.js +20 -11
- package/server/services/integrations/notion/provider.js +16 -13
- package/server/services/integrations/oauth_provider.js +115 -51
- package/server/services/integrations/registry.js +2 -0
- package/server/services/integrations/slack/provider.js +98 -9
- package/server/services/integrations/spotify/provider.js +67 -71
- package/server/services/integrations/trello/provider.js +21 -7
- package/server/services/integrations/weather/provider.js +18 -12
- package/server/services/integrations/whatsapp/provider.js +76 -16
- package/server/services/manager.js +110 -1
- package/server/services/memory/embedding_index.js +20 -8
- package/server/services/memory/embeddings.js +151 -90
- package/server/services/memory/ingestion.js +50 -9
- package/server/services/memory/ingestion_documents.js +13 -3
- package/server/services/memory/manager.js +52 -19
- package/server/services/messaging/automation.js +85 -10
- package/server/services/messaging/formatting_guides.js +7 -4
- package/server/services/messaging/http_platforms.js +33 -13
- package/server/services/messaging/inbound_queue.js +78 -24
- package/server/services/messaging/inbound_store.js +224 -0
- package/server/services/messaging/manager.js +326 -51
- package/server/services/messaging/typing_keepalive.js +5 -2
- package/server/services/messaging/whatsapp.js +22 -14
- package/server/services/network/http.js +210 -0
- package/server/services/network/safe_request.js +307 -0
- package/server/services/runtime/backends/local-vm.js +227 -67
- package/server/services/runtime/docker-vm-manager.js +9 -0
- package/server/services/runtime/guest_bootstrap.js +30 -4
- package/server/services/runtime/guest_image.js +43 -12
- package/server/services/runtime/manager.js +77 -23
- package/server/services/runtime/validation.js +7 -6
- package/server/services/security/tool_categories.js +6 -0
- package/server/services/social_reach/channels/github.js +10 -4
- package/server/services/social_reach/channels/reddit.js +4 -4
- package/server/services/social_reach/channels/rss.js +2 -2
- package/server/services/social_reach/channels/social_video.js +12 -7
- package/server/services/social_reach/channels/v2ex.js +21 -8
- package/server/services/social_reach/channels/x.js +2 -2
- package/server/services/social_reach/channels/xueqiu.js +5 -5
- package/server/services/social_reach/service.js +9 -6
- package/server/services/social_reach/utils.js +65 -14
- package/server/services/social_video/service.js +160 -50
- package/server/services/tasks/integration_runtime.js +18 -8
- package/server/services/tasks/runtime.js +39 -4
- package/server/services/voice/agentBridge.js +17 -4
- package/server/services/voice/bufferedLiveRelayAdapter.js +5 -0
- package/server/services/voice/liveSession.js +31 -0
- package/server/services/voice/message.js +1 -1
- package/server/services/voice/openaiSpeech.js +33 -8
- package/server/services/voice/providers.js +233 -151
- package/server/services/voice/runtime.js +2 -2
- package/server/services/voice/runtimeManager.js +118 -20
- package/server/services/voice/turnRunner.js +6 -0
- package/server/services/wearable/firmware_manifest.js +51 -13
- package/server/services/wearable/service.js +1 -0
- package/server/utils/abort.js +96 -0
- package/server/utils/cloud-security.js +110 -3
- package/server/utils/files.js +31 -0
- package/server/utils/image_payload.js +95 -0
- package/server/utils/retry.js +107 -0
|
@@ -436,7 +436,9 @@ class _ChatPanelState extends State<ChatPanel> with WidgetsBindingObserver {
|
|
|
436
436
|
double? prevExtent;
|
|
437
437
|
void settleInitial(int framesLeft) {
|
|
438
438
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
439
|
-
if (!mounted ||
|
|
439
|
+
if (!mounted ||
|
|
440
|
+
generation != _scrollGeneration ||
|
|
441
|
+
!_scrollController.hasClients) {
|
|
440
442
|
return;
|
|
441
443
|
}
|
|
442
444
|
final pos = _scrollController.position;
|
|
@@ -463,6 +465,7 @@ class _ChatPanelState extends State<ChatPanel> with WidgetsBindingObserver {
|
|
|
463
465
|
settleInitial(framesLeft - 1);
|
|
464
466
|
});
|
|
465
467
|
}
|
|
468
|
+
|
|
466
469
|
settleInitial(120);
|
|
467
470
|
return;
|
|
468
471
|
}
|
|
@@ -511,7 +514,8 @@ class _ChatPanelState extends State<ChatPanel> with WidgetsBindingObserver {
|
|
|
511
514
|
if (messages.isEmpty) {
|
|
512
515
|
// Agent switch resets the settle tracker so the next batch triggers hiding.
|
|
513
516
|
_visibleMessageCountAtLastSettle = 0;
|
|
514
|
-
} else if (_visibleMessageCountAtLastSettle == 0 &&
|
|
517
|
+
} else if (_visibleMessageCountAtLastSettle == 0 &&
|
|
518
|
+
!_awaitingInitialScrollSettle) {
|
|
515
519
|
// Messages just went from 0 → N: hide until the scroll position settles.
|
|
516
520
|
_awaitingInitialScrollSettle = true;
|
|
517
521
|
}
|
|
@@ -2039,7 +2043,11 @@ class _IgnoredChatsPanel extends StatelessWidget {
|
|
|
2039
2043
|
const SizedBox(height: 3),
|
|
2040
2044
|
Text(
|
|
2041
2045
|
'These channels are permanently silenced. To receive messages from them, add them manually to the access policy for the relevant platform.',
|
|
2042
|
-
style: TextStyle(
|
|
2046
|
+
style: TextStyle(
|
|
2047
|
+
color: _textSecondary,
|
|
2048
|
+
fontSize: 13,
|
|
2049
|
+
height: 1.4,
|
|
2050
|
+
),
|
|
2043
2051
|
),
|
|
2044
2052
|
],
|
|
2045
2053
|
),
|
|
@@ -2049,50 +2057,58 @@ class _IgnoredChatsPanel extends StatelessWidget {
|
|
|
2049
2057
|
),
|
|
2050
2058
|
const SizedBox(height: 14),
|
|
2051
2059
|
for (final key in ignored)
|
|
2052
|
-
Builder(
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2060
|
+
Builder(
|
|
2061
|
+
builder: (context) {
|
|
2062
|
+
final sep = key.indexOf(':');
|
|
2063
|
+
final platform = sep > 0 ? key.substring(0, sep) : key;
|
|
2064
|
+
final chatId = sep > 0 ? key.substring(sep + 1) : '';
|
|
2065
|
+
return Container(
|
|
2066
|
+
margin: const EdgeInsets.only(bottom: 8),
|
|
2067
|
+
padding: const EdgeInsets.symmetric(
|
|
2068
|
+
horizontal: 14,
|
|
2069
|
+
vertical: 10,
|
|
2070
|
+
),
|
|
2071
|
+
decoration: BoxDecoration(
|
|
2072
|
+
color: _bgSecondary,
|
|
2073
|
+
borderRadius: BorderRadius.circular(8),
|
|
2074
|
+
border: Border.all(color: _borderLight),
|
|
2075
|
+
),
|
|
2076
|
+
child: Row(
|
|
2077
|
+
children: [
|
|
2078
|
+
Icon(Icons.block_rounded, size: 16, color: _textMuted),
|
|
2079
|
+
const SizedBox(width: 10),
|
|
2080
|
+
Expanded(
|
|
2081
|
+
child: Column(
|
|
2082
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
2083
|
+
children: [
|
|
2084
|
+
Text(
|
|
2085
|
+
platform.toUpperCase(),
|
|
2086
|
+
style: TextStyle(
|
|
2087
|
+
color: _textMuted,
|
|
2088
|
+
fontSize: 10,
|
|
2089
|
+
fontWeight: FontWeight.w700,
|
|
2090
|
+
letterSpacing: 0.6,
|
|
2091
|
+
),
|
|
2079
2092
|
),
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2093
|
+
Text(
|
|
2094
|
+
chatId.isNotEmpty ? chatId : platform,
|
|
2095
|
+
style: TextStyle(
|
|
2096
|
+
color: _textPrimary,
|
|
2097
|
+
fontSize: 13,
|
|
2098
|
+
),
|
|
2099
|
+
),
|
|
2100
|
+
],
|
|
2101
|
+
),
|
|
2086
2102
|
),
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
)
|
|
2094
|
-
|
|
2095
|
-
|
|
2103
|
+
TextButton(
|
|
2104
|
+
onPressed: () => controller.removeIgnoredChat(key),
|
|
2105
|
+
child: Text('Remove'),
|
|
2106
|
+
),
|
|
2107
|
+
],
|
|
2108
|
+
),
|
|
2109
|
+
);
|
|
2110
|
+
},
|
|
2111
|
+
),
|
|
2096
2112
|
],
|
|
2097
2113
|
),
|
|
2098
2114
|
);
|
|
@@ -2236,7 +2252,9 @@ class _ConnectionReconnectingBanner extends StatelessWidget {
|
|
|
2236
2252
|
|
|
2237
2253
|
@override
|
|
2238
2254
|
Widget build(BuildContext context) {
|
|
2239
|
-
final msg = hasNetwork
|
|
2255
|
+
final msg = hasNetwork
|
|
2256
|
+
? 'Reconnecting to server…'
|
|
2257
|
+
: 'No network connection';
|
|
2240
2258
|
return Container(
|
|
2241
2259
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
|
2242
2260
|
decoration: BoxDecoration(
|
|
@@ -2308,7 +2326,6 @@ class _PendingApprovalBannerState extends State<_PendingApprovalBanner> {
|
|
|
2308
2326
|
|
|
2309
2327
|
int _remaining() {
|
|
2310
2328
|
final expiry = widget.approval.expiresAt;
|
|
2311
|
-
if (expiry == null) return 30;
|
|
2312
2329
|
return expiry.difference(DateTime.now()).inSeconds.clamp(0, 300);
|
|
2313
2330
|
}
|
|
2314
2331
|
|
|
@@ -2340,11 +2357,7 @@ class _PendingApprovalBannerState extends State<_PendingApprovalBanner> {
|
|
|
2340
2357
|
color: _accent.withValues(alpha: 0.16),
|
|
2341
2358
|
shape: BoxShape.circle,
|
|
2342
2359
|
),
|
|
2343
|
-
child: Icon(
|
|
2344
|
-
Icons.security_outlined,
|
|
2345
|
-
size: 13,
|
|
2346
|
-
color: _accent,
|
|
2347
|
-
),
|
|
2360
|
+
child: Icon(Icons.security_outlined, size: 13, color: _accent),
|
|
2348
2361
|
),
|
|
2349
2362
|
),
|
|
2350
2363
|
const SizedBox(width: 10),
|
|
@@ -3685,61 +3698,6 @@ class _MessagingMiniPill extends StatelessWidget {
|
|
|
3685
3698
|
}
|
|
3686
3699
|
}
|
|
3687
3700
|
|
|
3688
|
-
class _RunsMetricsStrip extends StatelessWidget {
|
|
3689
|
-
const _RunsMetricsStrip({required this.runs, required this.totalLoaded});
|
|
3690
|
-
|
|
3691
|
-
final List<RunSummary> runs;
|
|
3692
|
-
final int totalLoaded;
|
|
3693
|
-
|
|
3694
|
-
@override
|
|
3695
|
-
Widget build(BuildContext context) {
|
|
3696
|
-
final running = runs.where((run) => run.status == 'running').length;
|
|
3697
|
-
final failed = runs.where((run) => run.isFailure).length;
|
|
3698
|
-
final completed = runs.where((run) => run.status == 'completed').length;
|
|
3699
|
-
final tokens = runs.fold<int>(0, (sum, run) => sum + run.totalTokens);
|
|
3700
|
-
|
|
3701
|
-
return Wrap(
|
|
3702
|
-
spacing: 12,
|
|
3703
|
-
runSpacing: 12,
|
|
3704
|
-
children: <Widget>[
|
|
3705
|
-
_RunMetricCard(
|
|
3706
|
-
title: 'Showing',
|
|
3707
|
-
value: '${runs.length}',
|
|
3708
|
-
helper: totalLoaded == runs.length
|
|
3709
|
-
? 'Recent runs loaded'
|
|
3710
|
-
: 'Filtered from $totalLoaded loaded runs',
|
|
3711
|
-
color: _info,
|
|
3712
|
-
),
|
|
3713
|
-
_RunMetricCard(
|
|
3714
|
-
title: 'Completed',
|
|
3715
|
-
value: '$completed',
|
|
3716
|
-
helper: 'Finished successfully',
|
|
3717
|
-
color: _success,
|
|
3718
|
-
),
|
|
3719
|
-
_RunMetricCard(
|
|
3720
|
-
title: 'Failed',
|
|
3721
|
-
value: '$failed',
|
|
3722
|
-
helper: 'Need attention',
|
|
3723
|
-
color: _danger,
|
|
3724
|
-
),
|
|
3725
|
-
_RunMetricCard(
|
|
3726
|
-
title: 'Tokens',
|
|
3727
|
-
value: _formatNumber(tokens),
|
|
3728
|
-
helper: 'Across visible runs',
|
|
3729
|
-
color: _accentHover,
|
|
3730
|
-
),
|
|
3731
|
-
if (running > 0)
|
|
3732
|
-
_RunMetricCard(
|
|
3733
|
-
title: 'Running',
|
|
3734
|
-
value: '$running',
|
|
3735
|
-
helper: 'Still in progress',
|
|
3736
|
-
color: _warning,
|
|
3737
|
-
),
|
|
3738
|
-
],
|
|
3739
|
-
);
|
|
3740
|
-
}
|
|
3741
|
-
}
|
|
3742
|
-
|
|
3743
3701
|
class _RunMetricCard extends StatelessWidget {
|
|
3744
3702
|
const _RunMetricCard({
|
|
3745
3703
|
required this.title,
|
|
@@ -3787,323 +3745,6 @@ class _RunMetricCard extends StatelessWidget {
|
|
|
3787
3745
|
}
|
|
3788
3746
|
}
|
|
3789
3747
|
|
|
3790
|
-
class _RunsFilterBar extends StatelessWidget {
|
|
3791
|
-
const _RunsFilterBar({
|
|
3792
|
-
required this.searchController,
|
|
3793
|
-
required this.statusFilter,
|
|
3794
|
-
required this.onStatusChanged,
|
|
3795
|
-
});
|
|
3796
|
-
|
|
3797
|
-
final TextEditingController searchController;
|
|
3798
|
-
final String statusFilter;
|
|
3799
|
-
final ValueChanged<String> onStatusChanged;
|
|
3800
|
-
|
|
3801
|
-
@override
|
|
3802
|
-
Widget build(BuildContext context) {
|
|
3803
|
-
const filters = <String>['all', 'running', 'completed', 'failed'];
|
|
3804
|
-
return Card(
|
|
3805
|
-
child: Padding(
|
|
3806
|
-
padding: const EdgeInsets.all(18),
|
|
3807
|
-
child: Column(
|
|
3808
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
3809
|
-
children: <Widget>[
|
|
3810
|
-
const _SectionTitle('Filter Runs'),
|
|
3811
|
-
const SizedBox(height: 12),
|
|
3812
|
-
TextField(
|
|
3813
|
-
controller: searchController,
|
|
3814
|
-
decoration: InputDecoration(
|
|
3815
|
-
prefixIcon: Icon(Icons.search),
|
|
3816
|
-
hintText: 'Search title, model, trigger, error, or run id',
|
|
3817
|
-
suffixIcon: searchController.text.trim().isEmpty
|
|
3818
|
-
? null
|
|
3819
|
-
: IconButton(
|
|
3820
|
-
tooltip: 'Clear search',
|
|
3821
|
-
onPressed: searchController.clear,
|
|
3822
|
-
icon: Icon(Icons.close),
|
|
3823
|
-
),
|
|
3824
|
-
),
|
|
3825
|
-
),
|
|
3826
|
-
const SizedBox(height: 14),
|
|
3827
|
-
Wrap(
|
|
3828
|
-
spacing: 10,
|
|
3829
|
-
runSpacing: 10,
|
|
3830
|
-
children: filters.map((filter) {
|
|
3831
|
-
return FilterChip(
|
|
3832
|
-
label: Text(_titleCase(filter)),
|
|
3833
|
-
selected: statusFilter == filter,
|
|
3834
|
-
selectedColor: _accentMuted,
|
|
3835
|
-
checkmarkColor: _accent,
|
|
3836
|
-
backgroundColor: _bgSecondary,
|
|
3837
|
-
side: BorderSide(color: _border),
|
|
3838
|
-
onSelected: (_) => onStatusChanged(filter),
|
|
3839
|
-
);
|
|
3840
|
-
}).toList(),
|
|
3841
|
-
),
|
|
3842
|
-
],
|
|
3843
|
-
),
|
|
3844
|
-
),
|
|
3845
|
-
);
|
|
3846
|
-
}
|
|
3847
|
-
}
|
|
3848
|
-
|
|
3849
|
-
class _RunsHistoryPane extends StatelessWidget {
|
|
3850
|
-
const _RunsHistoryPane({
|
|
3851
|
-
required this.runs,
|
|
3852
|
-
required this.selectedRunId,
|
|
3853
|
-
required this.onSelect,
|
|
3854
|
-
});
|
|
3855
|
-
|
|
3856
|
-
final List<RunSummary> runs;
|
|
3857
|
-
final String? selectedRunId;
|
|
3858
|
-
final ValueChanged<String> onSelect;
|
|
3859
|
-
|
|
3860
|
-
@override
|
|
3861
|
-
Widget build(BuildContext context) {
|
|
3862
|
-
return Card(
|
|
3863
|
-
child: Padding(
|
|
3864
|
-
padding: const EdgeInsets.all(18),
|
|
3865
|
-
child: Column(
|
|
3866
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
3867
|
-
children: <Widget>[
|
|
3868
|
-
Row(
|
|
3869
|
-
children: <Widget>[
|
|
3870
|
-
Expanded(child: _SectionTitle('Run History')),
|
|
3871
|
-
Text(
|
|
3872
|
-
'${runs.length} items',
|
|
3873
|
-
style: TextStyle(color: _textSecondary),
|
|
3874
|
-
),
|
|
3875
|
-
],
|
|
3876
|
-
),
|
|
3877
|
-
const SizedBox(height: 12),
|
|
3878
|
-
...runs.map((run) {
|
|
3879
|
-
return Padding(
|
|
3880
|
-
padding: const EdgeInsets.only(bottom: 10),
|
|
3881
|
-
child: _RunHistoryRow(
|
|
3882
|
-
run: run,
|
|
3883
|
-
selected: run.id == selectedRunId,
|
|
3884
|
-
onTap: () => onSelect(run.id),
|
|
3885
|
-
),
|
|
3886
|
-
);
|
|
3887
|
-
}),
|
|
3888
|
-
],
|
|
3889
|
-
),
|
|
3890
|
-
),
|
|
3891
|
-
);
|
|
3892
|
-
}
|
|
3893
|
-
}
|
|
3894
|
-
|
|
3895
|
-
class _RunHistoryRow extends StatelessWidget {
|
|
3896
|
-
const _RunHistoryRow({
|
|
3897
|
-
required this.run,
|
|
3898
|
-
required this.selected,
|
|
3899
|
-
required this.onTap,
|
|
3900
|
-
});
|
|
3901
|
-
|
|
3902
|
-
final RunSummary run;
|
|
3903
|
-
final bool selected;
|
|
3904
|
-
final VoidCallback onTap;
|
|
3905
|
-
|
|
3906
|
-
@override
|
|
3907
|
-
Widget build(BuildContext context) {
|
|
3908
|
-
return InkWell(
|
|
3909
|
-
borderRadius: BorderRadius.circular(16),
|
|
3910
|
-
onTap: onTap,
|
|
3911
|
-
child: Container(
|
|
3912
|
-
padding: const EdgeInsets.all(14),
|
|
3913
|
-
decoration: BoxDecoration(
|
|
3914
|
-
color: selected ? _accentMuted : _bgSecondary,
|
|
3915
|
-
borderRadius: BorderRadius.circular(16),
|
|
3916
|
-
border: Border.all(color: selected ? _accent : _border),
|
|
3917
|
-
),
|
|
3918
|
-
child: Row(
|
|
3919
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
3920
|
-
children: <Widget>[
|
|
3921
|
-
Container(
|
|
3922
|
-
width: 12,
|
|
3923
|
-
height: 12,
|
|
3924
|
-
margin: const EdgeInsets.only(top: 5),
|
|
3925
|
-
decoration: BoxDecoration(
|
|
3926
|
-
color: run.statusColor,
|
|
3927
|
-
shape: BoxShape.circle,
|
|
3928
|
-
),
|
|
3929
|
-
),
|
|
3930
|
-
const SizedBox(width: 12),
|
|
3931
|
-
Expanded(
|
|
3932
|
-
child: Column(
|
|
3933
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
3934
|
-
children: <Widget>[
|
|
3935
|
-
Text(
|
|
3936
|
-
run.title,
|
|
3937
|
-
maxLines: 2,
|
|
3938
|
-
overflow: TextOverflow.ellipsis,
|
|
3939
|
-
style: TextStyle(fontWeight: FontWeight.w700, height: 1.2),
|
|
3940
|
-
),
|
|
3941
|
-
const SizedBox(height: 6),
|
|
3942
|
-
Text(
|
|
3943
|
-
'${run.triggerLabel} • ${run.createdAtLabel}${run.durationLabel == 'In progress' ? '' : ' • ${run.durationLabel}'}',
|
|
3944
|
-
style: TextStyle(color: _textSecondary, fontSize: 12),
|
|
3945
|
-
),
|
|
3946
|
-
const SizedBox(height: 4),
|
|
3947
|
-
Text(
|
|
3948
|
-
'${run.modelLabel} • ${run.totalTokensLabel} tokens',
|
|
3949
|
-
style: TextStyle(color: _textSecondary, fontSize: 12),
|
|
3950
|
-
),
|
|
3951
|
-
if (run.deliverableType.trim().isNotEmpty) ...<Widget>[
|
|
3952
|
-
const SizedBox(height: 4),
|
|
3953
|
-
Text(
|
|
3954
|
-
'Deliverable • ${run.deliverableType.replaceAll('_', ' ')}',
|
|
3955
|
-
style: TextStyle(color: _accent, fontSize: 12),
|
|
3956
|
-
),
|
|
3957
|
-
],
|
|
3958
|
-
if (run.error.trim().isNotEmpty) ...<Widget>[
|
|
3959
|
-
const SizedBox(height: 8),
|
|
3960
|
-
Text(
|
|
3961
|
-
run.error,
|
|
3962
|
-
maxLines: 2,
|
|
3963
|
-
overflow: TextOverflow.ellipsis,
|
|
3964
|
-
style: TextStyle(
|
|
3965
|
-
color: _danger,
|
|
3966
|
-
fontSize: 12,
|
|
3967
|
-
height: 1.4,
|
|
3968
|
-
),
|
|
3969
|
-
),
|
|
3970
|
-
],
|
|
3971
|
-
],
|
|
3972
|
-
),
|
|
3973
|
-
),
|
|
3974
|
-
const SizedBox(width: 10),
|
|
3975
|
-
Column(
|
|
3976
|
-
crossAxisAlignment: CrossAxisAlignment.end,
|
|
3977
|
-
children: <Widget>[
|
|
3978
|
-
_StatusPill(label: run.statusLabel, color: run.statusColor),
|
|
3979
|
-
const SizedBox(height: 12),
|
|
3980
|
-
Icon(
|
|
3981
|
-
Icons.chevron_right,
|
|
3982
|
-
color: selected ? _textPrimary : _textSecondary,
|
|
3983
|
-
),
|
|
3984
|
-
],
|
|
3985
|
-
),
|
|
3986
|
-
],
|
|
3987
|
-
),
|
|
3988
|
-
),
|
|
3989
|
-
);
|
|
3990
|
-
}
|
|
3991
|
-
}
|
|
3992
|
-
|
|
3993
|
-
class _RunDetailWorkspace extends StatelessWidget {
|
|
3994
|
-
const _RunDetailWorkspace({
|
|
3995
|
-
required this.run,
|
|
3996
|
-
required this.detail,
|
|
3997
|
-
required this.errorMessage,
|
|
3998
|
-
required this.loading,
|
|
3999
|
-
required this.onDelete,
|
|
4000
|
-
required this.onCopyResponse,
|
|
4001
|
-
});
|
|
4002
|
-
|
|
4003
|
-
final RunSummary? run;
|
|
4004
|
-
final RunDetailSnapshot? detail;
|
|
4005
|
-
final String? errorMessage;
|
|
4006
|
-
final bool loading;
|
|
4007
|
-
final Future<void> Function() onDelete;
|
|
4008
|
-
final Future<void> Function(String response) onCopyResponse;
|
|
4009
|
-
|
|
4010
|
-
@override
|
|
4011
|
-
Widget build(BuildContext context) {
|
|
4012
|
-
if (run == null) {
|
|
4013
|
-
return const _EmptyCard(
|
|
4014
|
-
title: 'Select a run',
|
|
4015
|
-
subtitle: 'Pick a run from the history list to inspect its steps.',
|
|
4016
|
-
);
|
|
4017
|
-
}
|
|
4018
|
-
|
|
4019
|
-
final selectedRun = run!;
|
|
4020
|
-
final snapshot = detail;
|
|
4021
|
-
return Column(
|
|
4022
|
-
children: <Widget>[
|
|
4023
|
-
_RunHeroCard(run: selectedRun, onDelete: onDelete),
|
|
4024
|
-
const SizedBox(height: 16),
|
|
4025
|
-
if (loading && snapshot == null)
|
|
4026
|
-
Card(
|
|
4027
|
-
child: Padding(
|
|
4028
|
-
padding: EdgeInsets.all(24),
|
|
4029
|
-
child: Row(
|
|
4030
|
-
children: <Widget>[
|
|
4031
|
-
SizedBox.square(
|
|
4032
|
-
dimension: 20,
|
|
4033
|
-
child: CircularProgressIndicator(strokeWidth: 2),
|
|
4034
|
-
),
|
|
4035
|
-
SizedBox(width: 12),
|
|
4036
|
-
Text(
|
|
4037
|
-
'Loading run detail...',
|
|
4038
|
-
style: TextStyle(color: _textSecondary),
|
|
4039
|
-
),
|
|
4040
|
-
],
|
|
4041
|
-
),
|
|
4042
|
-
),
|
|
4043
|
-
)
|
|
4044
|
-
else if (errorMessage case final message?) ...<Widget>[
|
|
4045
|
-
_InlineError(message: message),
|
|
4046
|
-
const SizedBox(height: 16),
|
|
4047
|
-
] else if (snapshot != null) ...<Widget>[
|
|
4048
|
-
Wrap(
|
|
4049
|
-
spacing: 12,
|
|
4050
|
-
runSpacing: 12,
|
|
4051
|
-
children: <Widget>[
|
|
4052
|
-
_RunMetricCard(
|
|
4053
|
-
title: 'Steps',
|
|
4054
|
-
value: '${snapshot.steps.length}',
|
|
4055
|
-
helper: 'Recorded events',
|
|
4056
|
-
color: _info,
|
|
4057
|
-
),
|
|
4058
|
-
_RunMetricCard(
|
|
4059
|
-
title: 'Completed tools',
|
|
4060
|
-
value: '${snapshot.completedTools}',
|
|
4061
|
-
helper: 'Successful tool calls',
|
|
4062
|
-
color: _success,
|
|
4063
|
-
),
|
|
4064
|
-
_RunMetricCard(
|
|
4065
|
-
title: 'Failures',
|
|
4066
|
-
value: '${snapshot.failedTools}',
|
|
4067
|
-
helper: 'Tool errors',
|
|
4068
|
-
color: _danger,
|
|
4069
|
-
),
|
|
4070
|
-
_RunMetricCard(
|
|
4071
|
-
title: 'Helpers',
|
|
4072
|
-
value: '${snapshot.helperCount}',
|
|
4073
|
-
helper: 'Subagents or helpers',
|
|
4074
|
-
color: _accentHover,
|
|
4075
|
-
),
|
|
4076
|
-
_RunMetricCard(
|
|
4077
|
-
title: 'Trace events',
|
|
4078
|
-
value: '${snapshot.events.length}',
|
|
4079
|
-
helper: 'Structured run timeline',
|
|
4080
|
-
color: _warning,
|
|
4081
|
-
),
|
|
4082
|
-
],
|
|
4083
|
-
),
|
|
4084
|
-
const SizedBox(height: 16),
|
|
4085
|
-
_RunResponseCard(
|
|
4086
|
-
response: snapshot.response,
|
|
4087
|
-
onCopy: () => onCopyResponse(snapshot.response),
|
|
4088
|
-
),
|
|
4089
|
-
if (snapshot.run.deliverableType.trim().isNotEmpty) ...<Widget>[
|
|
4090
|
-
const SizedBox(height: 16),
|
|
4091
|
-
_DeliverableSummaryCard(run: snapshot.run),
|
|
4092
|
-
],
|
|
4093
|
-
const SizedBox(height: 16),
|
|
4094
|
-
_RunTimelineCard(steps: snapshot.steps, loading: loading),
|
|
4095
|
-
const SizedBox(height: 16),
|
|
4096
|
-
_RunEventTimelineCard(events: snapshot.events, loading: loading),
|
|
4097
|
-
] else
|
|
4098
|
-
const _EmptyCard(
|
|
4099
|
-
title: 'No detail available',
|
|
4100
|
-
subtitle: 'This run does not have step detail yet.',
|
|
4101
|
-
),
|
|
4102
|
-
],
|
|
4103
|
-
);
|
|
4104
|
-
}
|
|
4105
|
-
}
|
|
4106
|
-
|
|
4107
3748
|
class _RunHeroCard extends StatelessWidget {
|
|
4108
3749
|
const _RunHeroCard({required this.run, required this.onDelete});
|
|
4109
3750
|
|
|
@@ -4165,22 +3806,13 @@ class _RunHeroCard extends StatelessWidget {
|
|
|
4165
3806
|
spacing: 6,
|
|
4166
3807
|
runSpacing: 6,
|
|
4167
3808
|
children: <Widget>[
|
|
4168
|
-
_MetaPill(
|
|
4169
|
-
|
|
4170
|
-
icon: Icons.bolt_outlined,
|
|
4171
|
-
),
|
|
4172
|
-
_MetaPill(
|
|
4173
|
-
label: run.modelLabel,
|
|
4174
|
-
icon: Icons.memory_outlined,
|
|
4175
|
-
),
|
|
3809
|
+
_MetaPill(label: run.triggerLabel, icon: Icons.bolt_outlined),
|
|
3810
|
+
_MetaPill(label: run.modelLabel, icon: Icons.memory_outlined),
|
|
4176
3811
|
_MetaPill(
|
|
4177
3812
|
label: run.createdAtLabel,
|
|
4178
3813
|
icon: Icons.schedule_outlined,
|
|
4179
3814
|
),
|
|
4180
|
-
_MetaPill(
|
|
4181
|
-
label: run.durationLabel,
|
|
4182
|
-
icon: Icons.timer_outlined,
|
|
4183
|
-
),
|
|
3815
|
+
_MetaPill(label: run.durationLabel, icon: Icons.timer_outlined),
|
|
4184
3816
|
if (run.totalTokensLabel.isNotEmpty)
|
|
4185
3817
|
_MetaPill(
|
|
4186
3818
|
label: '${run.totalTokensLabel} tok',
|
|
@@ -4357,259 +3989,6 @@ class _DeliverableSummaryCard extends StatelessWidget {
|
|
|
4357
3989
|
}
|
|
4358
3990
|
}
|
|
4359
3991
|
|
|
4360
|
-
class _RunTimelineCard extends StatelessWidget {
|
|
4361
|
-
const _RunTimelineCard({required this.steps, required this.loading});
|
|
4362
|
-
|
|
4363
|
-
final List<RunStepItem> steps;
|
|
4364
|
-
final bool loading;
|
|
4365
|
-
|
|
4366
|
-
@override
|
|
4367
|
-
Widget build(BuildContext context) {
|
|
4368
|
-
return Card(
|
|
4369
|
-
child: Padding(
|
|
4370
|
-
padding: const EdgeInsets.all(18),
|
|
4371
|
-
child: Column(
|
|
4372
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
4373
|
-
children: <Widget>[
|
|
4374
|
-
Row(
|
|
4375
|
-
children: <Widget>[
|
|
4376
|
-
Expanded(child: _SectionTitle('Step Timeline')),
|
|
4377
|
-
if (loading)
|
|
4378
|
-
const SizedBox.square(
|
|
4379
|
-
dimension: 16,
|
|
4380
|
-
child: CircularProgressIndicator(strokeWidth: 2),
|
|
4381
|
-
),
|
|
4382
|
-
],
|
|
4383
|
-
),
|
|
4384
|
-
const SizedBox(height: 12),
|
|
4385
|
-
if (steps.isEmpty)
|
|
4386
|
-
Text(
|
|
4387
|
-
'No run steps recorded yet.',
|
|
4388
|
-
style: TextStyle(color: _textSecondary),
|
|
4389
|
-
)
|
|
4390
|
-
else
|
|
4391
|
-
...steps.map((step) {
|
|
4392
|
-
return Padding(
|
|
4393
|
-
padding: const EdgeInsets.only(bottom: 10),
|
|
4394
|
-
child: _RunStepCard(step: step),
|
|
4395
|
-
);
|
|
4396
|
-
}),
|
|
4397
|
-
],
|
|
4398
|
-
),
|
|
4399
|
-
),
|
|
4400
|
-
);
|
|
4401
|
-
}
|
|
4402
|
-
}
|
|
4403
|
-
|
|
4404
|
-
class _RunEventTimelineCard extends StatelessWidget {
|
|
4405
|
-
const _RunEventTimelineCard({required this.events, required this.loading});
|
|
4406
|
-
|
|
4407
|
-
final List<RunEventItem> events;
|
|
4408
|
-
final bool loading;
|
|
4409
|
-
|
|
4410
|
-
@override
|
|
4411
|
-
Widget build(BuildContext context) {
|
|
4412
|
-
return Card(
|
|
4413
|
-
child: Padding(
|
|
4414
|
-
padding: const EdgeInsets.all(18),
|
|
4415
|
-
child: Column(
|
|
4416
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
4417
|
-
children: <Widget>[
|
|
4418
|
-
Row(
|
|
4419
|
-
children: <Widget>[
|
|
4420
|
-
Expanded(child: _SectionTitle('Execution Trace')),
|
|
4421
|
-
if (loading)
|
|
4422
|
-
const SizedBox.square(
|
|
4423
|
-
dimension: 16,
|
|
4424
|
-
child: CircularProgressIndicator(strokeWidth: 2),
|
|
4425
|
-
),
|
|
4426
|
-
],
|
|
4427
|
-
),
|
|
4428
|
-
const SizedBox(height: 12),
|
|
4429
|
-
if (events.isEmpty)
|
|
4430
|
-
Text(
|
|
4431
|
-
'No structured run events recorded yet.',
|
|
4432
|
-
style: TextStyle(color: _textSecondary),
|
|
4433
|
-
)
|
|
4434
|
-
else
|
|
4435
|
-
...events.map((event) {
|
|
4436
|
-
return Padding(
|
|
4437
|
-
padding: const EdgeInsets.only(bottom: 10),
|
|
4438
|
-
child: _RunEventCard(event: event),
|
|
4439
|
-
);
|
|
4440
|
-
}),
|
|
4441
|
-
],
|
|
4442
|
-
),
|
|
4443
|
-
),
|
|
4444
|
-
);
|
|
4445
|
-
}
|
|
4446
|
-
}
|
|
4447
|
-
|
|
4448
|
-
class _RunEventCard extends StatelessWidget {
|
|
4449
|
-
const _RunEventCard({required this.event});
|
|
4450
|
-
|
|
4451
|
-
final RunEventItem event;
|
|
4452
|
-
|
|
4453
|
-
@override
|
|
4454
|
-
Widget build(BuildContext context) {
|
|
4455
|
-
final accent = event.isFailure ? _danger : _info;
|
|
4456
|
-
return Container(
|
|
4457
|
-
padding: const EdgeInsets.all(14),
|
|
4458
|
-
decoration: BoxDecoration(
|
|
4459
|
-
color: _bgSecondary,
|
|
4460
|
-
borderRadius: BorderRadius.circular(16),
|
|
4461
|
-
border: Border.all(color: accent.withValues(alpha: 0.24)),
|
|
4462
|
-
),
|
|
4463
|
-
child: Column(
|
|
4464
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
4465
|
-
children: <Widget>[
|
|
4466
|
-
Row(
|
|
4467
|
-
children: <Widget>[
|
|
4468
|
-
Container(
|
|
4469
|
-
width: 10,
|
|
4470
|
-
height: 10,
|
|
4471
|
-
decoration: BoxDecoration(
|
|
4472
|
-
color: accent,
|
|
4473
|
-
shape: BoxShape.circle,
|
|
4474
|
-
),
|
|
4475
|
-
),
|
|
4476
|
-
const SizedBox(width: 10),
|
|
4477
|
-
Expanded(
|
|
4478
|
-
child: Text(
|
|
4479
|
-
event.title,
|
|
4480
|
-
style: const TextStyle(fontWeight: FontWeight.w700),
|
|
4481
|
-
),
|
|
4482
|
-
),
|
|
4483
|
-
if (event.createdAtLabel.isNotEmpty)
|
|
4484
|
-
Text(
|
|
4485
|
-
event.createdAtLabel,
|
|
4486
|
-
style: TextStyle(color: _textSecondary, fontSize: 12),
|
|
4487
|
-
),
|
|
4488
|
-
],
|
|
4489
|
-
),
|
|
4490
|
-
if (event.detail.trim().isNotEmpty) ...<Widget>[
|
|
4491
|
-
const SizedBox(height: 8),
|
|
4492
|
-
Text(
|
|
4493
|
-
event.detail,
|
|
4494
|
-
style: TextStyle(color: _textSecondary, height: 1.4),
|
|
4495
|
-
),
|
|
4496
|
-
],
|
|
4497
|
-
],
|
|
4498
|
-
),
|
|
4499
|
-
);
|
|
4500
|
-
}
|
|
4501
|
-
}
|
|
4502
|
-
|
|
4503
|
-
class _RunStepCard extends StatelessWidget {
|
|
4504
|
-
const _RunStepCard({required this.step});
|
|
4505
|
-
|
|
4506
|
-
final RunStepItem step;
|
|
4507
|
-
|
|
4508
|
-
@override
|
|
4509
|
-
Widget build(BuildContext context) {
|
|
4510
|
-
final theme = Theme.of(context);
|
|
4511
|
-
return Container(
|
|
4512
|
-
decoration: BoxDecoration(
|
|
4513
|
-
color: _bgSecondary,
|
|
4514
|
-
borderRadius: BorderRadius.circular(16),
|
|
4515
|
-
border: Border.all(color: _border),
|
|
4516
|
-
),
|
|
4517
|
-
child: Theme(
|
|
4518
|
-
data: theme.copyWith(dividerColor: Colors.transparent),
|
|
4519
|
-
child: ExpansionTile(
|
|
4520
|
-
tilePadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
|
4521
|
-
childrenPadding: const EdgeInsets.fromLTRB(14, 0, 14, 14),
|
|
4522
|
-
initiallyExpanded:
|
|
4523
|
-
step.status == 'failed' || step.status == 'running',
|
|
4524
|
-
leading: Container(
|
|
4525
|
-
width: 34,
|
|
4526
|
-
height: 34,
|
|
4527
|
-
decoration: BoxDecoration(
|
|
4528
|
-
color: step.statusColor.withValues(alpha: 0.16),
|
|
4529
|
-
shape: BoxShape.circle,
|
|
4530
|
-
),
|
|
4531
|
-
child: Center(
|
|
4532
|
-
child: Text(
|
|
4533
|
-
'${step.displayIndex}',
|
|
4534
|
-
style: TextStyle(
|
|
4535
|
-
color: step.statusColor,
|
|
4536
|
-
fontWeight: FontWeight.w800,
|
|
4537
|
-
),
|
|
4538
|
-
),
|
|
4539
|
-
),
|
|
4540
|
-
),
|
|
4541
|
-
title: Column(
|
|
4542
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
4543
|
-
children: <Widget>[
|
|
4544
|
-
Text(step.label, style: TextStyle(fontWeight: FontWeight.w700)),
|
|
4545
|
-
const SizedBox(height: 6),
|
|
4546
|
-
Text(
|
|
4547
|
-
step.summary,
|
|
4548
|
-
maxLines: 2,
|
|
4549
|
-
overflow: TextOverflow.ellipsis,
|
|
4550
|
-
style: TextStyle(
|
|
4551
|
-
color: _textSecondary,
|
|
4552
|
-
fontSize: 12,
|
|
4553
|
-
height: 1.45,
|
|
4554
|
-
),
|
|
4555
|
-
),
|
|
4556
|
-
const SizedBox(height: 8),
|
|
4557
|
-
Wrap(
|
|
4558
|
-
spacing: 8,
|
|
4559
|
-
runSpacing: 8,
|
|
4560
|
-
children: <Widget>[
|
|
4561
|
-
_StatusPill(label: step.statusLabel, color: step.statusColor),
|
|
4562
|
-
_MetaPill(label: step.typeLabel, icon: Icons.layers_outlined),
|
|
4563
|
-
if (step.startedAt != null)
|
|
4564
|
-
_MetaPill(
|
|
4565
|
-
label: step.startedAtLabel!,
|
|
4566
|
-
icon: Icons.schedule_outlined,
|
|
4567
|
-
),
|
|
4568
|
-
if (step.durationLabel != null)
|
|
4569
|
-
_MetaPill(
|
|
4570
|
-
label: step.durationLabel!,
|
|
4571
|
-
icon: Icons.timer_outlined,
|
|
4572
|
-
),
|
|
4573
|
-
if (step.tokensUsed > 0)
|
|
4574
|
-
_MetaPill(
|
|
4575
|
-
label: '${_formatNumber(step.tokensUsed)} tokens',
|
|
4576
|
-
icon: Icons.toll_outlined,
|
|
4577
|
-
),
|
|
4578
|
-
],
|
|
4579
|
-
),
|
|
4580
|
-
],
|
|
4581
|
-
),
|
|
4582
|
-
children: <Widget>[
|
|
4583
|
-
if (step.description.trim().isNotEmpty &&
|
|
4584
|
-
step.description.trim() != step.summary.trim())
|
|
4585
|
-
_RunDetailBlock(label: 'Description', value: step.description),
|
|
4586
|
-
if (step.inputSummary.trim().isNotEmpty)
|
|
4587
|
-
_RunDetailBlock(label: 'Input summary', value: step.inputSummary),
|
|
4588
|
-
if (step.toolInput.trim().isNotEmpty)
|
|
4589
|
-
_RunDetailBlock(
|
|
4590
|
-
label: 'Tool input',
|
|
4591
|
-
value: _truncateRunText(step.toolInput),
|
|
4592
|
-
monospace: true,
|
|
4593
|
-
),
|
|
4594
|
-
if (step.error.trim().isNotEmpty)
|
|
4595
|
-
_RunDetailBlock(
|
|
4596
|
-
label: 'Error',
|
|
4597
|
-
value: step.error,
|
|
4598
|
-
monospace: true,
|
|
4599
|
-
)
|
|
4600
|
-
else if (step.result.trim().isNotEmpty)
|
|
4601
|
-
_RunDetailBlock(
|
|
4602
|
-
label: 'Result',
|
|
4603
|
-
value: _truncateRunText(step.result),
|
|
4604
|
-
monospace: true,
|
|
4605
|
-
),
|
|
4606
|
-
],
|
|
4607
|
-
),
|
|
4608
|
-
),
|
|
4609
|
-
);
|
|
4610
|
-
}
|
|
4611
|
-
}
|
|
4612
|
-
|
|
4613
3992
|
class _RunDetailBlock extends StatelessWidget {
|
|
4614
3993
|
const _RunDetailBlock({
|
|
4615
3994
|
required this.label,
|
|
@@ -4855,10 +4234,7 @@ class _RunGraphEdgePainter extends CustomPainter {
|
|
|
4855
4234
|
..strokeWidth = 1.8
|
|
4856
4235
|
..strokeCap = StrokeCap.round;
|
|
4857
4236
|
|
|
4858
|
-
final start = Offset(
|
|
4859
|
-
from.x + _FlowNode.w,
|
|
4860
|
-
from.y + _FlowNode.h / 2,
|
|
4861
|
-
);
|
|
4237
|
+
final start = Offset(from.x + _FlowNode.w, from.y + _FlowNode.h / 2);
|
|
4862
4238
|
final end = Offset(to.x, to.y + _FlowNode.h / 2);
|
|
4863
4239
|
|
|
4864
4240
|
if (from.y != to.y) {
|
|
@@ -4971,10 +4347,7 @@ class _FlowNodeWidget extends StatelessWidget {
|
|
|
4971
4347
|
const SizedBox(width: 5),
|
|
4972
4348
|
Text(
|
|
4973
4349
|
isSpecial ? node.nodeType : node.status,
|
|
4974
|
-
style: TextStyle(
|
|
4975
|
-
color: _textSecondary,
|
|
4976
|
-
fontSize: 11,
|
|
4977
|
-
),
|
|
4350
|
+
style: TextStyle(color: _textSecondary, fontSize: 11),
|
|
4978
4351
|
),
|
|
4979
4352
|
],
|
|
4980
4353
|
),
|
|
@@ -5093,10 +4466,7 @@ class _RunFlowGraphCanvasState extends State<_RunFlowGraphCanvas> {
|
|
|
5093
4466
|
Expanded(
|
|
5094
4467
|
child: Text(
|
|
5095
4468
|
'$stepCount step${stepCount == 1 ? '' : 's'} · tap a node to inspect',
|
|
5096
|
-
style: TextStyle(
|
|
5097
|
-
color: _textSecondary,
|
|
5098
|
-
fontSize: 12.5,
|
|
5099
|
-
),
|
|
4469
|
+
style: TextStyle(color: _textSecondary, fontSize: 12.5),
|
|
5100
4470
|
),
|
|
5101
4471
|
),
|
|
5102
4472
|
if (widget.loading)
|
|
@@ -5116,8 +4486,14 @@ class _RunFlowGraphCanvasState extends State<_RunFlowGraphCanvas> {
|
|
|
5116
4486
|
vertical: 6,
|
|
5117
4487
|
),
|
|
5118
4488
|
),
|
|
5119
|
-
icon: const Icon(
|
|
5120
|
-
|
|
4489
|
+
icon: const Icon(
|
|
4490
|
+
Icons.center_focus_strong_outlined,
|
|
4491
|
+
size: 15,
|
|
4492
|
+
),
|
|
4493
|
+
label: const Text(
|
|
4494
|
+
'Reset view',
|
|
4495
|
+
style: TextStyle(fontSize: 12),
|
|
4496
|
+
),
|
|
5121
4497
|
),
|
|
5122
4498
|
],
|
|
5123
4499
|
),
|
|
@@ -5155,7 +4531,9 @@ class _RunFlowGraphCanvasState extends State<_RunFlowGraphCanvas> {
|
|
|
5155
4531
|
node: node,
|
|
5156
4532
|
selected: node.id == widget.selectedNodeId,
|
|
5157
4533
|
onTap: () => widget.onNodeSelected(
|
|
5158
|
-
node.id == widget.selectedNodeId
|
|
4534
|
+
node.id == widget.selectedNodeId
|
|
4535
|
+
? null
|
|
4536
|
+
: node.id,
|
|
5159
4537
|
),
|
|
5160
4538
|
),
|
|
5161
4539
|
),
|
|
@@ -5312,32 +4690,31 @@ class _RunSelectorRail extends StatelessWidget {
|
|
|
5312
4690
|
style: TextStyle(color: _textSecondary, fontSize: 12),
|
|
5313
4691
|
),
|
|
5314
4692
|
)
|
|
5315
|
-
else
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
style: TextStyle(color: _textSecondary, fontSize: 11),
|
|
5338
|
-
),
|
|
4693
|
+
else ...<Widget>[
|
|
4694
|
+
ListView.separated(
|
|
4695
|
+
shrinkWrap: true,
|
|
4696
|
+
physics: const NeverScrollableScrollPhysics(),
|
|
4697
|
+
itemCount: math.min(filteredRuns.length, 40),
|
|
4698
|
+
separatorBuilder: (_, __) => const SizedBox(height: 6),
|
|
4699
|
+
itemBuilder: (context, index) {
|
|
4700
|
+
final run = filteredRuns[index];
|
|
4701
|
+
final isSelected = run.id == selectedRunId;
|
|
4702
|
+
return _RunSelectorRow(
|
|
4703
|
+
run: run,
|
|
4704
|
+
selected: isSelected,
|
|
4705
|
+
onTap: () => onSelect(run.id),
|
|
4706
|
+
);
|
|
4707
|
+
},
|
|
4708
|
+
),
|
|
4709
|
+
if (filteredRuns.length > 40)
|
|
4710
|
+
Padding(
|
|
4711
|
+
padding: const EdgeInsets.only(top: 8),
|
|
4712
|
+
child: Text(
|
|
4713
|
+
'Showing 40 of ${filteredRuns.length} — use search to narrow results',
|
|
4714
|
+
style: TextStyle(color: _textSecondary, fontSize: 11),
|
|
5339
4715
|
),
|
|
5340
|
-
|
|
4716
|
+
),
|
|
4717
|
+
],
|
|
5341
4718
|
],
|
|
5342
4719
|
),
|
|
5343
4720
|
),
|
|
@@ -5396,10 +4773,7 @@ class _RunSelectorRow extends StatelessWidget {
|
|
|
5396
4773
|
const SizedBox(height: 3),
|
|
5397
4774
|
Text(
|
|
5398
4775
|
'${run.createdAtLabel} · ${run.durationLabel}',
|
|
5399
|
-
style: TextStyle(
|
|
5400
|
-
color: _textSecondary,
|
|
5401
|
-
fontSize: 11,
|
|
5402
|
-
),
|
|
4776
|
+
style: TextStyle(color: _textSecondary, fontSize: 11),
|
|
5403
4777
|
),
|
|
5404
4778
|
],
|
|
5405
4779
|
),
|
|
@@ -5522,7 +4896,11 @@ class _RunSelectedStepCard extends StatelessWidget {
|
|
|
5522
4896
|
color: color.withValues(alpha: 0.14),
|
|
5523
4897
|
borderRadius: BorderRadius.circular(10),
|
|
5524
4898
|
),
|
|
5525
|
-
child: Icon(
|
|
4899
|
+
child: Icon(
|
|
4900
|
+
Icons.build_circle_outlined,
|
|
4901
|
+
size: 18,
|
|
4902
|
+
color: color,
|
|
4903
|
+
),
|
|
5526
4904
|
),
|
|
5527
4905
|
const SizedBox(width: 10),
|
|
5528
4906
|
Expanded(
|
|
@@ -5539,10 +4917,7 @@ class _RunSelectedStepCard extends StatelessWidget {
|
|
|
5539
4917
|
const SizedBox(height: 2),
|
|
5540
4918
|
Text(
|
|
5541
4919
|
step.typeLabel,
|
|
5542
|
-
style: TextStyle(
|
|
5543
|
-
color: _textSecondary,
|
|
5544
|
-
fontSize: 12,
|
|
5545
|
-
),
|
|
4920
|
+
style: TextStyle(color: _textSecondary, fontSize: 12),
|
|
5546
4921
|
),
|
|
5547
4922
|
],
|
|
5548
4923
|
),
|
|
@@ -5583,7 +4958,11 @@ class _RunSelectedStepCard extends StatelessWidget {
|
|
|
5583
4958
|
],
|
|
5584
4959
|
if (step.error.trim().isNotEmpty) ...<Widget>[
|
|
5585
4960
|
const SizedBox(height: 8),
|
|
5586
|
-
_RunDetailBlock(
|
|
4961
|
+
_RunDetailBlock(
|
|
4962
|
+
label: 'Error',
|
|
4963
|
+
value: step.error,
|
|
4964
|
+
monospace: true,
|
|
4965
|
+
),
|
|
5587
4966
|
] else if (step.result.trim().isNotEmpty) ...<Widget>[
|
|
5588
4967
|
const SizedBox(height: 8),
|
|
5589
4968
|
_RunDetailBlock(
|