agent_servers: First-class Oh My Pi (omp) harness integration
Promotes the Oh My Pi (omp) agent harness to a first-class agent: - Auto-detects `omp` in PATH when not explicitly configured - Maps display name to "Oh My Pi" across agent panel, conversation views, and settings - Defaults session mode to "task" - Injects Zed context environment variables (`ZED=1`, `SURFACE=zed`, `ZED_VERSION`, `ZED_PROJECT_PATHS`) - Adds documentation and example in default.json - Preserves full compatibility with ACP subagent session info and task hierarchies Release Notes: - Added first-class integration and auto-detection for the Oh My Pi (omp) agent harness.
This commit is contained in:
1 parent
a210572a9d
commit
05b31a44ce
4 files changed
+104
-3
No files matched your search
@@ -1,3 +1,5 @@
|
||||
> [!IMPORTANT]
|
||||
> Remove this line to confirm you've reviewed this PR before submitting.
|
||||
# Zed
|
||||
|
||||
[](https://zed.dev)
|
||||
|
||||
@@ -2851,6 +2851,14 @@
|
||||
// Configures context servers for use by the agent.
|
||||
"context_servers": {},
|
||||
// Configures agent servers available in the agent panel.
|
||||
// Example for Oh My Pi (omp) harness:
|
||||
// "agent_servers": {
|
||||
// "omp": {
|
||||
// "type": "custom",
|
||||
// "command": "omp",
|
||||
// "args": ["acp"]
|
||||
// }
|
||||
// },
|
||||
"agent_servers": {},
|
||||
"debugger": {
|
||||
"stepping_granularity": "line",
|
||||
|
||||
@@ -18,6 +18,9 @@ pub const GEMINI_ID: &str = "gemini";
|
||||
pub const CLAUDE_AGENT_ID: &str = "claude-acp";
|
||||
pub const CODEX_ID: &str = "codex-acp";
|
||||
pub const CURSOR_ID: &str = "cursor";
|
||||
pub const OMP_ID: &str = "omp";
|
||||
pub const OMP_NAME: &str = "Oh My Pi";
|
||||
|
||||
|
||||
/// A generic agent server implementation for custom user-defined agents
|
||||
pub struct CustomAgentServer {
|
||||
@@ -37,7 +40,7 @@ impl AgentServer for CustomAgentServer {
|
||||
|
||||
fn logo(&self) -> IconName {
|
||||
match self.agent_id.0.as_ref() {
|
||||
"omp" => IconName::Omp,
|
||||
OMP_ID => IconName::Omp,
|
||||
_ => IconName::Terminal,
|
||||
}
|
||||
}
|
||||
@@ -53,8 +56,14 @@ impl AgentServer for CustomAgentServer {
|
||||
settings
|
||||
.as_ref()
|
||||
.and_then(|s| s.default_mode().map(acp::SessionModeId::new))
|
||||
.or_else(|| {
|
||||
if self.agent_id().0.as_ref() == OMP_ID {
|
||||
Some(acp::SessionModeId::new("task"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn favorite_config_option_value_ids(
|
||||
&self,
|
||||
config_id: &acp::SessionConfigId,
|
||||
@@ -248,6 +257,26 @@ impl AgentServer for CustomAgentServer {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if agent_id.as_ref() == OMP_ID {
|
||||
extra_env.insert("ZED".to_owned(), "1".to_owned());
|
||||
extra_env.insert("SURFACE".to_owned(), "zed".to_owned());
|
||||
extra_env.insert(
|
||||
"ZED_VERSION".to_owned(),
|
||||
release_channel::AppVersion::global(cx).to_string(),
|
||||
);
|
||||
let project_paths: Vec<String> = project
|
||||
.read(cx)
|
||||
.worktrees(cx)
|
||||
.filter_map(|w| {
|
||||
w.read(cx)
|
||||
.as_local()
|
||||
.map(|l| l.abs_path().to_string_lossy().into_owned())
|
||||
})
|
||||
.collect();
|
||||
if !project_paths.is_empty() {
|
||||
extra_env.insert("ZED_PROJECT_PATHS".to_owned(), project_paths.join(":"));
|
||||
}
|
||||
}
|
||||
let store = delegate.store.downgrade();
|
||||
cx.spawn(async move |cx| {
|
||||
if is_registry_agent && agent_id.as_ref() == GEMINI_ID {
|
||||
@@ -431,4 +460,17 @@ mod tests {
|
||||
assert!(is_registry_agent("agent-from-settings", cx));
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_omp_agent_server_attributes(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
let server = CustomAgentServer::new(AgentId::new(OMP_ID));
|
||||
assert_eq!(server.logo(), IconName::Omp);
|
||||
cx.update(|cx| {
|
||||
assert_eq!(
|
||||
server.default_mode(cx),
|
||||
Some(acp::SessionModeId::new("task"))
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -191,6 +191,9 @@ pub struct AgentServerStore {
|
||||
pub struct AgentServersUpdated;
|
||||
|
||||
impl EventEmitter<AgentServersUpdated> for AgentServerStore {}
|
||||
pub const OMP_ID: &str = "omp";
|
||||
pub const OMP_NAME: &str = "Oh My Pi";
|
||||
|
||||
|
||||
static EXTENSION_TO_REGISTRY_IDS: LazyLock<HashMap<&'static str, &'static str>> =
|
||||
LazyLock::new(|| {
|
||||
@@ -256,6 +259,10 @@ impl AgentServerStore {
|
||||
self.external_agents
|
||||
.get(name)
|
||||
.and_then(|entry| entry.display_name.clone())
|
||||
.or_else(|| match name.0.as_ref() {
|
||||
OMP_ID => Some(SharedString::from(OMP_NAME)),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn init_remote(session: &AnyProtoClient) {
|
||||
@@ -360,6 +367,10 @@ impl AgentServerStore {
|
||||
match settings {
|
||||
CustomAgentServerSettings::Custom { command, .. } => {
|
||||
let agent_name = AgentId(name.clone().into());
|
||||
let display_name = match name.as_str() {
|
||||
OMP_ID => Some(SharedString::from(OMP_NAME)),
|
||||
_ => None,
|
||||
};
|
||||
self.external_agents.insert(
|
||||
agent_name.clone(),
|
||||
ExternalAgentEntry::new(
|
||||
@@ -368,7 +379,7 @@ impl AgentServerStore {
|
||||
project_environment: project_environment.clone(),
|
||||
}) as Box<dyn ExternalAgentServer>,
|
||||
ExternalAgentSource::Custom,
|
||||
None,
|
||||
display_name,
|
||||
None,
|
||||
),
|
||||
);
|
||||
@@ -444,6 +455,31 @@ impl AgentServerStore {
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-detect Oh My Pi (omp) harness if not explicitly configured in settings
|
||||
let omp_agent_id = AgentId(OMP_ID.into());
|
||||
if !self.external_agents.contains_key(&omp_agent_id)
|
||||
&& !new_settings.contains_key(OMP_ID)
|
||||
{
|
||||
if let Ok(omp_path) = which::which(OMP_ID) {
|
||||
self.external_agents.insert(
|
||||
omp_agent_id,
|
||||
ExternalAgentEntry::new(
|
||||
Box::new(LocalCustomAgent {
|
||||
command: AgentServerCommand {
|
||||
path: omp_path,
|
||||
args: vec!["acp".to_string()],
|
||||
env: None,
|
||||
},
|
||||
project_environment: project_environment.clone(),
|
||||
}) as Box<dyn ExternalAgentServer>,
|
||||
ExternalAgentSource::Custom,
|
||||
Some(SharedString::from(OMP_NAME)),
|
||||
None,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// For each rebuilt versioned agent, compare the version. If it
|
||||
// changed, notify the active connection to reconnect. Otherwise,
|
||||
// transfer the channel to the new entry so future updates can use it.
|
||||
@@ -2337,4 +2373,17 @@ mod tests {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_omp_agent_display_name(_cx: &mut TestAppContext) {
|
||||
let store = AgentServerStore::collab();
|
||||
assert_eq!(
|
||||
store.agent_display_name(&AgentId::new(OMP_ID)),
|
||||
Some(SharedString::from(OMP_NAME))
|
||||
);
|
||||
assert_eq!(
|
||||
store.agent_display_name(&AgentId::new("random_agent")),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user