Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:55:02.443Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

panel.rs

325 lines · 11.2 KB · rust
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
Served at tenant.openagents/omega Member data and write actions are omitted.