use std::{
ffi::OsString,
path::{Path, PathBuf},
};
use agent_client_protocol::schema::v1::{
AuthMethod, AuthMethodId, Meta, RequestPermissionRequest, SessionModeId,
};
use serde_json::Value;
use crate::{
acp_runtime::{
permission::PermissionDecision, AcpAgentPolicy, AcpSpawnPlan, ExtensionAnswer,
SessionConfigChoice,
},
agent::AntigravityTool,
cli_runtime::{status_sink::RuntimeLabel, CliExecutable},
permission::PermissionMode,
};
use super::{
executable,
fence::{self, AllowedTools},
home::AntigravityHome,
ANTIGRAVITY_LABEL,
};
const DEFAULT_MODE: &str = "default";
const MODEL_CONFIG_ID: &str = "model";
pub(crate) struct AntigravityAcpPolicy {
model: Option<String>,
permission_mode: PermissionMode,
tools: Vec<AntigravityTool>,
cwd: PathBuf,
home: AntigravityHome,
spawn_env: Vec<(OsString, OsString)>,
allowed: Option<AllowedTools>,
}
impl AntigravityAcpPolicy {
pub(crate) fn new(
model: Option<String>,
permission_mode: PermissionMode,
tools: Vec<AntigravityTool>,
cwd: PathBuf,
home: AntigravityHome,
spawn_env: Vec<(OsString, OsString)>,
) -> Self {
Self {
model,
permission_mode,
tools,
cwd,
home,
spawn_env,
allowed: None,
}
}
}
impl AcpAgentPolicy for AntigravityAcpPolicy {
fn label(&self) -> RuntimeLabel {
ANTIGRAVITY_LABEL
}
fn resolve_executable(&self) -> Result<CliExecutable, String> {
executable::resolve().map_err(|error| error.to_string())
}
fn spawn_plan(&self, _frozen: Option<Vec<String>>) -> Result<AcpSpawnPlan, String> {
Ok(AcpSpawnPlan {
argv: executable::server_args(),
cwd: self.cwd.clone(),
env: self.spawn_env.clone(),
})
}
fn prepare(&mut self, _run_dir: &Path) -> Result<Vec<String>, String> {
let allowed = fence::map_permission_mode(self.permission_mode, &self.tools)
.map_err(|error| error.to_string())?;
self.home.status().require_signed_in()?;
self.allowed = Some(allowed);
Ok(Vec::new())
}
fn log_path(&self, output_file: &Path) -> PathBuf {
output_file.with_file_name(crate::subagent::LOG_FILE_NAME)
}
fn auth_method(&self, _advertised: &[AuthMethod]) -> Option<AuthMethodId> {
None
}
fn session_mode(&self) -> Option<SessionModeId> {
Some(SessionModeId::new(DEFAULT_MODE))
}
fn session_meta(&self) -> Option<Meta> {
self.allowed.as_ref().map(AllowedTools::session_meta)
}
fn session_config(&self) -> Vec<SessionConfigChoice> {
self.model
.iter()
.map(|model| SessionConfigChoice {
id: MODEL_CONFIG_ID.into(),
value: model.clone().into(),
})
.collect()
}
fn decide_permission(&self, request: &RequestPermissionRequest) -> PermissionDecision {
match &self.allowed {
Some(allowed) => fence::decide(allowed, self.permission_mode, request),
None => PermissionDecision::Reject,
}
}
fn answer_extension(&self, _method: &str, _params: &Value) -> Option<ExtensionAnswer> {
None
}
}
#[cfg(test)]
#[path = "policy_tests.rs"]
mod tests;