Skip to content

Commit 7f5a689

Browse files
jasonkeungoz-agent
andauthored
REMOTE-1601 Add named agent API key support behind FeatureFlag::NamedAgents (#10390)
## Description Add support for creating API keys scoped to named agent identities in the Oz Cloud API Keys settings page, gated behind `FeatureFlag::NamedAgents` (dogfood-enabled). When the flag is enabled, the "Team" option in the key-type selector is replaced with "Agent". Users can select which named agent the key authenticates as from a dropdown populated via `GET /api/v1/agent/identities`. If no agents exist, an empty state links to `oz.warp.dev/agents` to create one. ### Changes - **`crates/warp_features`**: Add `FeatureFlag::NamedAgents` to dogfood - **`crates/warp_graphql_schema`** + **`crates/graphql`**: Add `agentUid: ID` to `GenerateApiKeyInput` - **`app/src/server/server_api/auth.rs`**: Add `AgentIdentity` type, `list_agent_identities()`, and `agent_uid` param to `create_api_key()` - **`app/src/settings_view/platform/create_api_key_modal.rs`**: Agent type selector, dropdown, empty state, create flow - **`app/src/settings_view/platform_page.rs`**: Scope column displays "Agent" when flag is on [Oz conversation](https://staging.warp.dev/conversation/d5027d88-07f2-482e-a2eb-39879d66fc9e) · [Plan](https://staging.warp.dev/drive/notebook/OC5WmSrFQZwF9BAYEhWMM6) ## Linked Issue - [ ] The linked issue is labeled `ready-to-spec` or `ready-to-implement`. - [x] Where appropriate, screenshots or a short video of the implementation are included below (especially for user-visible or UI changes). https://www.loom.com/share/636237a795c34e1ca1806761b5018160 ## Testing Feature is behind `FeatureFlag::NamedAgents` (dogfood only). Verified compilation of `warp_features` and `warp_graphql` crates. Full app compilation blocked by pre-existing `http_client` error unrelated to this change. ## Agent Mode - [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode Co-Authored-By: Oz <oz-agent@warp.dev> --------- Co-authored-by: Oz <oz-agent@warp.dev>
1 parent f85d69a commit 7f5a689

8 files changed

Lines changed: 232 additions & 37 deletions

File tree

app/src/ai/agent_sdk/driver/git_credentials.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ pub(crate) fn configure_git_identity(credentials: &[GitCredential]) {
241241
/// API call fails — these are transient failures worth retrying.
242242
async fn try_refresh(task_id: &str, ai_client: &Arc<dyn AIClient>) -> Result<()> {
243243
let workload_token =
244-
warp_isolation_platform::issue_workload_token(Some(Duration::from_mins(5)))
244+
warp_isolation_platform::issue_workload_token(Some(Duration::from_secs(5 * 60)))
245245
.await
246246
.context("Failed to issue workload token for git credentials refresh")?
247247
.token;

app/src/ai/agent_sdk/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,7 @@ impl AgentDriverRunner {
10311031
return Ok(vec![]);
10321032
}
10331033
let workload_token = match warp_isolation_platform::issue_workload_token(Some(
1034-
std::time::Duration::from_mins(5),
1034+
std::time::Duration::from_secs(5 * 60),
10351035
))
10361036
.await
10371037
{

app/src/server/server_api/auth.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@ use crate::{
6666

6767
use super::ServerApi;
6868

69+
/// A named agent identity from the public API.
70+
#[derive(Clone, Debug, serde::Deserialize)]
71+
pub struct AgentIdentity {
72+
pub uid: String,
73+
pub name: String,
74+
pub available: bool,
75+
}
76+
77+
/// Wrapper for the `GET /api/v1/agent/identities` response.
78+
#[derive(serde::Deserialize)]
79+
struct AgentIdentitiesResponse {
80+
agents: Vec<AgentIdentity>,
81+
}
82+
6983
/// Error messages returned from the Firebase REST API when attempting to convert a refresh token
7084
/// into an access token that indicate the user's token is in an errored state.
7185
/// These are "soft" errors because the user likely just needs to log in again.
@@ -201,11 +215,15 @@ pub trait AuthClient: 'static + Send + Sync {
201215
&self,
202216
name: String,
203217
team_id: Option<cynic::Id>,
218+
agent_uid: Option<cynic::Id>,
204219
expires_at: Option<warp_graphql::scalars::Time>,
205220
) -> Result<GenerateApiKeyResult>;
206221

207222
async fn expire_api_key(&self, key_uid: &ApiKeyUid) -> Result<ExpireApiKeyResult>;
208223

224+
/// Fetches the list of named agent identities for the user's team.
225+
async fn list_agent_identities(&self) -> Result<Vec<AgentIdentity>>;
226+
209227
/// Returns a cached ambient workload token, or issues a new one if not present or expired.
210228
///
211229
/// Returns `Ok(None)` if not running in an isolation platform (e.g., Namespace) or on WASM.
@@ -606,12 +624,14 @@ impl AuthClient for ServerApi {
606624
&self,
607625
name: String,
608626
team_id: Option<cynic::Id>,
627+
agent_uid: Option<cynic::Id>,
609628
expires_at: Option<warp_graphql::scalars::Time>,
610629
) -> Result<GenerateApiKeyResult> {
611630
let variables = GenerateApiKeyVariables {
612631
input: GenerateApiKeyInput {
613632
name,
614633
team_id,
634+
agent_uid,
615635
expires_at,
616636
},
617637
request_context: get_request_context(),
@@ -620,6 +640,12 @@ impl AuthClient for ServerApi {
620640
let response = self.send_graphql_request(operation, None).await?;
621641
Ok(response.generate_api_key)
622642
}
643+
644+
async fn list_agent_identities(&self) -> Result<Vec<AgentIdentity>> {
645+
let response: AgentIdentitiesResponse = self.get_public_api("agent/identities").await?;
646+
Ok(response.agents)
647+
}
648+
623649
async fn expire_api_key(&self, key_uid: &ApiKeyUid) -> Result<ExpireApiKeyResult> {
624650
let variables = ExpireApiKeyVariables {
625651
key_uid: key_uid.into(),

0 commit comments

Comments
 (0)