Skip to repository content325 lines · 11.2 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T02:55:02.443Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
panel.rs
1//! Bounded Agent Computer panel — start one cloud turn and show outcome.
2
3use anyhow::Result;
4use editor::Editor;
5use gpui::{
6 App, AsyncWindowContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
7 InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Task, WeakEntity,
8 Window, div, px,
9};
10use omega_effectd::{SharedOmegaEffectdSupervisor, shared_supervisor};
11use serde_json::json;
12use ui::{Button, ButtonStyle, Label, LabelSize, prelude::*};
13use workspace::{
14 Workspace,
15 dock::{DockPosition, Panel, PanelEvent},
16};
17use zed_actions::agent_computer::{OpenPanel, StartTurn};
18
19use crate::{DEFAULT_CONTROL_PLANE_BASE_URL, DEFAULT_REPO_REF};
20
21const PANEL_KEY: &str = "AgentComputerPanel";
22
23#[derive(Clone, Debug)]
24struct TurnOutcome {
25 session_ref: String,
26 state: String,
27 finish_reason: String,
28 artifact_ref: Option<String>,
29}
30
31pub struct AgentComputerPanel {
32 _workspace: WeakEntity<Workspace>,
33 focus_handle: FocusHandle,
34 objective_editor: Entity<Editor>,
35 repo_editor: Entity<Editor>,
36 status: SharedString,
37 outcome: Option<TurnOutcome>,
38 submitting: bool,
39 supervisor: Option<SharedOmegaEffectdSupervisor>,
40}
41
42pub fn init(cx: &mut App) {
43 cx.observe_new(|workspace: &mut Workspace, _, _| {
44 workspace
45 .register_action(|workspace, _: &OpenPanel, window, cx| {
46 workspace.focus_panel::<AgentComputerPanel>(window, cx);
47 })
48 .register_action(|workspace, _: &StartTurn, window, cx| {
49 if let Some(panel) = workspace.panel::<AgentComputerPanel>(cx) {
50 panel.update(cx, |panel, cx| panel.start_turn(cx));
51 }
52 workspace.focus_panel::<AgentComputerPanel>(window, cx);
53 });
54 })
55 .detach();
56}
57
58impl AgentComputerPanel {
59 pub fn load(
60 workspace: WeakEntity<Workspace>,
61 cx: AsyncWindowContext,
62 ) -> Task<Result<Entity<Self>>> {
63 cx.spawn(async move |cx| {
64 let workspace_for_panel = workspace.clone();
65 workspace.update_in(cx, |_workspace, window, cx| {
66 Ok(cx.new(|cx| Self::new(workspace_for_panel, window, cx)))
67 })?
68 })
69 }
70
71 fn new(workspace: WeakEntity<Workspace>, window: &mut Window, cx: &mut Context<Self>) -> Self {
72 let objective_editor = cx.new(|cx| {
73 let mut editor = Editor::multi_line(window, cx);
74 editor.set_placeholder_text(
75 "Public-safe objective for one Agent Computer turn.",
76 window,
77 cx,
78 );
79 editor
80 });
81 let repo_editor = cx.new(|cx| {
82 let mut editor = Editor::single_line(window, cx);
83 editor.set_text(DEFAULT_REPO_REF, window, cx);
84 editor
85 });
86 let mut panel = Self {
87 _workspace: workspace,
88 focus_handle: cx.focus_handle(),
89 objective_editor,
90 repo_editor,
91 status: "Agent Computer launches through omega-effectd only.".into(),
92 outcome: None,
93 submitting: false,
94 supervisor: None,
95 };
96 panel.ensure_supervisor(cx);
97 panel
98 }
99
100 fn ensure_supervisor(&mut self, cx: &mut Context<Self>) {
101 if self.supervisor.is_some() {
102 return;
103 }
104 match shared_supervisor(cx) {
105 Ok(supervisor) => self.supervisor = Some(supervisor),
106 Err(error) => {
107 self.status = format!("omega-effectd unavailable ({error}).").into();
108 }
109 }
110 cx.notify();
111 }
112
113 fn start_turn(&mut self, cx: &mut Context<Self>) {
114 if self.submitting {
115 return;
116 }
117 let objective = self.objective_editor.read(cx).text(cx).trim().to_string();
118 let repo_ref = self.repo_editor.read(cx).text(cx).trim().to_string();
119 if objective.is_empty() {
120 self.status = "Objective is required.".into();
121 cx.notify();
122 return;
123 }
124 if repo_ref.is_empty() {
125 self.status = "repoRef is required.".into();
126 cx.notify();
127 return;
128 }
129 let bearer = match std::env::var("OPENAGENTS_AGENT_TOKEN") {
130 Ok(token) if !token.trim().is_empty() => token,
131 _ => {
132 self.status =
133 "Set OPENAGENTS_AGENT_TOKEN in the environment (runtime-only; never stored)."
134 .into();
135 cx.notify();
136 return;
137 }
138 };
139 let control_plane = std::env::var("OPENAGENTS_CONTROL_PLANE_BASE_URL")
140 .unwrap_or_else(|_| DEFAULT_CONTROL_PLANE_BASE_URL.to_string());
141 self.ensure_supervisor(cx);
142 let Some(supervisor) = self.supervisor.clone() else {
143 self.status = "omega-effectd supervisor is unavailable.".into();
144 cx.notify();
145 return;
146 };
147
148 self.submitting = true;
149 self.outcome = None;
150 self.status = "Starting Agent Computer turn…".into();
151 cx.notify();
152
153 cx.spawn(async move |this, cx| {
154 let result = {
155 let mut guard = supervisor.lock().await;
156 match guard.ensure_started().await {
157 Ok(()) => guard
158 .run_agent_computer_turn(json!({
159 "bearerToken": bearer,
160 "controlPlaneBaseUrl": control_plane,
161 "repoRef": repo_ref,
162 "objective": objective,
163 }))
164 .await,
165 Err(error) => Err(omega_effectd::SupervisorError::Anyhow(error)),
166 }
167 };
168 this.update(cx, |panel, cx| {
169 panel.submitting = false;
170 match result {
171 Ok(value) => {
172 let session = value.get("session");
173 let session_ref = session
174 .and_then(|s| s.get("sessionRef"))
175 .and_then(|v| v.as_str())
176 .unwrap_or("unknown")
177 .to_string();
178 let state = session
179 .and_then(|s| s.get("state"))
180 .and_then(|v| v.as_str())
181 .unwrap_or("unknown")
182 .to_string();
183 let finish_reason = value
184 .get("finishReason")
185 .and_then(|v| v.as_str())
186 .unwrap_or("unknown")
187 .to_string();
188 let artifact_ref = session
189 .and_then(|s| s.get("artifactRef"))
190 .and_then(|v| v.as_str())
191 .map(str::to_string);
192 panel.outcome = Some(TurnOutcome {
193 session_ref: session_ref.clone(),
194 state: state.clone(),
195 finish_reason: finish_reason.clone(),
196 artifact_ref,
197 });
198 panel.status = format!(
199 "Turn finished: session={session_ref} state={state} finish={finish_reason}"
200 )
201 .into();
202 }
203 Err(error) => {
204 panel.status = format!("Agent Computer turn failed: {error}").into();
205 }
206 }
207 cx.notify();
208 })
209 .ok();
210 })
211 .detach();
212 }
213}
214
215impl EventEmitter<PanelEvent> for AgentComputerPanel {}
216
217impl Focusable for AgentComputerPanel {
218 fn focus_handle(&self, _: &App) -> FocusHandle {
219 self.focus_handle.clone()
220 }
221}
222
223impl Panel for AgentComputerPanel {
224 fn persistent_name() -> &'static str {
225 "AgentComputerPanel"
226 }
227
228 fn panel_key() -> &'static str {
229 PANEL_KEY
230 }
231
232 fn position(&self, _: &Window, _: &App) -> DockPosition {
233 DockPosition::Right
234 }
235
236 fn position_is_valid(&self, _: DockPosition) -> bool {
237 true
238 }
239
240 fn set_position(&mut self, _: DockPosition, _: &mut Window, _: &mut Context<Self>) {}
241
242 fn default_size(&self, _: &Window, _: &App) -> gpui::Pixels {
243 px(420.)
244 }
245
246 fn icon(&self, _: &Window, _: &App) -> Option<IconName> {
247 Some(IconName::OmegaAgent)
248 }
249
250 fn icon_tooltip(&self, _: &Window, _: &App) -> Option<&'static str> {
251 Some("Agent Computer")
252 }
253
254 fn toggle_action(&self) -> Box<dyn gpui::Action> {
255 Box::new(OpenPanel)
256 }
257
258 fn activation_priority(&self) -> u32 {
259 9
260 }
261}
262
263impl Render for AgentComputerPanel {
264 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
265 let can_start = !self.submitting && self.supervisor.is_some();
266 v_flex()
267 .id("agent-computer-panel")
268 .size_full()
269 .track_focus(&self.focus_handle)
270 .gap_2()
271 .p_3()
272 .child(Label::new("Agent Computer").size(LabelSize::Large))
273 .child(
274 Label::new(
275 "Starts one openagents_cloud turn through omega-effectd. Not Full Auto.",
276 )
277 .color(Color::Muted),
278 )
279 .child(Label::new(self.status.clone()).color(Color::Muted))
280 .child(Label::new("Objective"))
281 .child(
282 div()
283 .border_1()
284 .border_color(cx.theme().colors().border)
285 .rounded_md()
286 .h(px(100.))
287 .child(self.objective_editor.clone()),
288 )
289 .child(Label::new("repoRef"))
290 .child(
291 div()
292 .border_1()
293 .border_color(cx.theme().colors().border)
294 .rounded_md()
295 .child(self.repo_editor.clone()),
296 )
297 .child(
298 Button::new(
299 "agent-computer-start",
300 if self.submitting {
301 "Starting…"
302 } else {
303 "Start cloud turn"
304 },
305 )
306 .style(ButtonStyle::Filled)
307 .disabled(!can_start)
308 .on_click(cx.listener(|this, _, _, cx| this.start_turn(cx))),
309 )
310 .when_some(self.outcome.clone(), |this, outcome| {
311 this.child(Label::new(format!(
312 "session={} · state={} · finish={}{}",
313 outcome.session_ref,
314 outcome.state,
315 outcome.finish_reason,
316 outcome
317 .artifact_ref
318 .as_ref()
319 .map(|artifact| format!(" · artifact={artifact}"))
320 .unwrap_or_default()
321 )))
322 })
323 }
324}
325