返回 CodeWhale
operate.rs
根目录 / crates / tui / src / tui / setup / operate.rs
1 use crate::config::Config;
2 use crate::tui::app::App;
3
4 #[derive(Debug, Clone, PartialEq, Eq)]
5 pub(super) struct SetupOperateFacts {
6 pub(super) runtime_ready: bool,
7 pub(super) runtime_result: String,
8 pub(super) roster_ready: bool,
9 pub(super) roster_result: String,
10 pub(super) concurrency_result: String,
11 pub(super) result: String,
12 }
13
14 impl Default for SetupOperateFacts {
15 fn default() -> Self {
16 Self {
17 runtime_ready: false,
18 runtime_result: "agent runtime not loaded".to_string(),
19 roster_ready: false,
20 roster_result: "Fleet not loaded".to_string(),
21 concurrency_result: "concurrency not loaded".to_string(),
22 result: "operate readiness not loaded".to_string(),
23 }
24 }
25 }
26
27 impl SetupOperateFacts {
28 pub(super) fn from_app_config(app: &App, config: &Config, provider_ready: bool) -> Self {
29 let provider_identity = app.provider_identity_for_persistence();
30 let identity = app
31 .admitted_provider_identity()
32 .ok()
33 .filter(|identity| config.verify_provider_identity(identity).is_ok());
34 let subagents_enabled = identity.map_or(false, |identity| {
35 config.subagents_enabled_for_provider(identity)
36 });
37 let max_subagents =
38 identity.map_or(0, |identity| config.max_subagents_for_provider(identity));
39 let launch_concurrency = identity.map_or(0, |identity| {
40 config.launch_concurrency_for_provider(identity)
41 });
42 let max_admitted = identity.map_or(0, |identity| {
43 config.max_admitted_subagents_for_provider(identity)
44 });
45 let runtime_disabled_reason = if subagents_enabled {
46 None
47 } else {
48 Some(
49 config
50 .subagents_disabled_reason()
51 .unwrap_or("disabled for active provider"),
52 )
53 };
54 let max_spawn_depth = identity.map_or(0, |identity| {
55 config.subagent_max_spawn_depth_for_provider(identity)
56 });
57 let runtime_configured =
58 subagents_enabled && max_subagents > 0 && launch_concurrency > 0 && max_spawn_depth > 0;
59 // Conversation and read-only discovery do not require a worker. This
60 // readiness fact describes whether Operate can also dispatch executable
61 // work in the background and report its lifecycle accurately.
62 let runtime_ready = runtime_configured && provider_ready;
63 let runtime_result = if let Some(reason) = runtime_disabled_reason {
64 format!("agent runtime disabled ({reason})")
65 } else if runtime_ready {
66 format!(
67 "agent runtime ready for {}; max_subagents={}, launch_concurrency={}, admission={}, max_spawn_depth={}; background dispatch and completion receipts are available",
68 provider_identity, max_subagents, launch_concurrency, max_admitted, max_spawn_depth
69 )
70 } else if runtime_configured {
71 format!(
72 "agent runtime configured for {}, but the active provider route is not ready; max_subagents={}, launch_concurrency={}, admission={}, max_spawn_depth={}",
73 provider_identity, max_subagents, launch_concurrency, max_admitted, max_spawn_depth
74 )
75 } else {
76 format!(
77 "agent runtime has no launch capacity for {}; max_subagents={}, launch_concurrency={}, admission={}, max_spawn_depth={}",
78 provider_identity, max_subagents, launch_concurrency, max_admitted, max_spawn_depth
79 )
80 };
81
82 let roster = crate::fleet::identity::load_effective_roster(
83 &config.fleet_config(),
84 &app.workspace,
85 Some(app.plugin_registry.as_ref()),
86 );
87 let roster_members = roster.members().len();
88 let origin_count = |origin| {
89 roster
90 .members()
91 .iter()
92 .filter(|member| member.origin == origin)
93 .count()
94 };
95 let config_members = origin_count(crate::fleet::roster::ProfileOrigin::Config);
96 let personal_members = origin_count(crate::fleet::roster::ProfileOrigin::Personal);
97 let project_members = origin_count(crate::fleet::roster::ProfileOrigin::Workspace);
98 let custom_roster_members = config_members + personal_members + project_members;
99 let roster_ready = roster.load_error().is_none() && roster_members > 0;
100 let roster_result = if let Some(error) = roster.load_error() {
101 error.to_string()
102 } else if custom_roster_members > 0 {
103 let origins = [
104 ("config", config_members),
105 ("personal", personal_members),
106 ("project", project_members),
107 ]
108 .into_iter()
109 .filter(|(_, count)| *count > 0)
110 .map(|(label, count)| format!("{label}={count}"))
111 .collect::<Vec<_>>()
112 .join(", ");
113 format!("{roster_members} agents (custom: {origins})")
114 } else {
115 format!("{roster_members} built-in agents; starter Fleet available")
116 };
117
118 let concurrency_result = format!(
119 "configured launch_concurrency={launch_concurrency}; max_subagents={max_subagents}; admission={max_admitted}; plan limit not probed"
120 );
121 let result = format!(
122 "provider={}, runtime={}, roster={}, concurrency={}",
123 if provider_ready {
124 "ready"
125 } else {
126 "needs_action"
127 },
128 if runtime_ready {
129 "ready"
130 } else {
131 "needs_action"
132 },
133 if roster_ready {
134 "ready"
135 } else {
136 "needs_action"
137 },
138 concurrency_result
139 );
140
141 Self {
142 runtime_ready,
143 runtime_result,
144 roster_ready,
145 roster_result,
146 concurrency_result,
147 result,
148 }
149 }
150 }
151
151 lines RUST