Expose approval requests in admin logs
CI / UI Checks (push) Has been cancelled
CI / Frontend E2E (push) Has been cancelled
CI / Deployment Manifests (push) Has been cancelled
CI / Deploy (push) Has been cancelled
CI / Rust Checks (push) Has been cancelled

This commit is contained in:
github-ops
2026-06-24 13:51:54 +00:00
parent 7aad3b1228
commit 8b8f2fc6c5
14 changed files with 699 additions and 42 deletions
+15 -14
View File
@@ -28,10 +28,10 @@ pub mod requests {
CreatePlatformApiKeyRequest, CreateSecretRequest, CreateVersionRequest,
CreateWorkspaceRequest, CreateYamlImportJobRequest, DecideApprovalRequest,
ExpireApprovalRequest, FinishApprovalRequest, FinishImportJobRequest,
ListInvocationLogsQuery, PublishAgentRequest, PublishRequest, RotateSecretRequest,
SaveAgentBindingsRequest, SaveAuthProfileRequest, SaveDescriptorMetadataRequest,
SaveSampleMetadataRequest, SaveWorkspaceUpstreamRequest, UpdateWorkspaceRequest,
UsageQuery,
ListApprovalRequestsQuery, ListInvocationLogsQuery, PublishAgentRequest, PublishRequest,
RotateSecretRequest, SaveAgentBindingsRequest, SaveAuthProfileRequest,
SaveDescriptorMetadataRequest, SaveSampleMetadataRequest, SaveWorkspaceUpstreamRequest,
UpdateWorkspaceRequest, UsageQuery,
};
}
@@ -47,15 +47,16 @@ pub use model::{
CreatePlatformApiKeyRequest, CreateSecretRequest, CreateVersionRequest, CreateWorkspaceRequest,
CreateYamlImportJobRequest, DecideApprovalRequest, DescriptorKind, DescriptorMetadata,
ExpireApprovalRequest, FinishApprovalRequest, FinishImportJobRequest, ImportJob, ImportJobId,
ImportJobKind, ImportJobStatus, InvitationRecord, InvocationLogRecord, ListInvocationLogsQuery,
MembershipRecord, OperationAgentRef, OperationSampleMetadata, OperationSummary,
OperationUsageSummary, OperationVersionRecord, Page, PlatformApiKeyRecord, PublishAgentRequest,
PublishRequest, PublishedAgentTool, RegistryOperation, RotateSecretRequest, SampleKind,
SaveAgentBindingsRequest, SaveAuthProfileRequest, SaveDescriptorMetadataRequest,
SaveSampleMetadataRequest, SaveWorkspaceUpstreamRequest, SecretRecord, SecretVersionRecord,
SessionRecord, UpdateWorkspaceRequest, UsageAgentBreakdown, UsageBucket,
UsageOperationBreakdown, UsageQuery, UsageRollupRecord, UsageSummary, UsageTimelinePoint,
WorkspaceMembershipRecord, WorkspaceRecord, WorkspaceUpstream, WorkspaceUpstreamId,
YamlImportJob, YamlImportJobCompletion, YamlImportJobId, YamlImportJobStatus,
ImportJobKind, ImportJobStatus, InvitationRecord, InvocationLogRecord,
ListApprovalRequestsQuery, ListInvocationLogsQuery, MembershipRecord, OperationAgentRef,
OperationSampleMetadata, OperationSummary, OperationUsageSummary, OperationVersionRecord, Page,
PlatformApiKeyRecord, PublishAgentRequest, PublishRequest, PublishedAgentTool,
RegistryOperation, RotateSecretRequest, SampleKind, SaveAgentBindingsRequest,
SaveAuthProfileRequest, SaveDescriptorMetadataRequest, SaveSampleMetadataRequest,
SaveWorkspaceUpstreamRequest, SecretRecord, SecretVersionRecord, SessionRecord,
UpdateWorkspaceRequest, UsageAgentBreakdown, UsageBucket, UsageOperationBreakdown, UsageQuery,
UsageRollupRecord, UsageSummary, UsageTimelinePoint, WorkspaceMembershipRecord,
WorkspaceRecord, WorkspaceUpstream, WorkspaceUpstreamId, YamlImportJob,
YamlImportJobCompletion, YamlImportJobId, YamlImportJobStatus,
};
pub use postgres::{PostgresPoolConfig, PostgresPoolConfigError, PostgresRegistry};
+7
View File
@@ -524,6 +524,13 @@ pub struct CreateApprovalRequest<'a> {
pub approval: &'a ApprovalRequest,
}
#[derive(Clone, Debug)]
pub struct ListApprovalRequestsQuery<'a> {
pub workspace_id: &'a WorkspaceId,
pub status: Option<ApprovalRequestStatus>,
pub limit: u32,
}
#[derive(Clone, Debug)]
pub struct DecideApprovalRequest<'a> {
pub workspace_id: &'a WorkspaceId,
@@ -140,6 +140,83 @@ impl PostgresRegistry {
row.map(map_approval_request_row).transpose()
}
pub async fn list_approval_requests(
&self,
query: ListApprovalRequestsQuery<'_>,
) -> Result<Vec<ApprovalRequestRecord>, RegistryError> {
let status = query
.status
.map(|status| serialize_enum_text(&status, "approval_status"))
.transpose()?;
let rows = sqlx::query(
"select
id,
workspace_id,
agent_id,
operation_id,
operation_version,
status,
risk_level,
confirmation_title,
confirmation_body,
request_payload_json,
response_payload_json,
created_at,
expires_at,
decided_at,
decided_by_key_id,
decision_note
from approval_requests
where workspace_id = $1
and ($2::text is null or status = $2)
order by created_at desc
limit $3",
)
.bind(query.workspace_id.as_str())
.bind(status.as_deref())
.bind(i64::from(query.limit))
.fetch_all(&self.pool)
.await?;
rows.into_iter().map(map_approval_request_row).collect()
}
pub async fn get_approval_request(
&self,
workspace_id: &WorkspaceId,
approval_id: &ApprovalRequestId,
) -> Result<Option<ApprovalRequestRecord>, RegistryError> {
let row = sqlx::query(
"select
id,
workspace_id,
agent_id,
operation_id,
operation_version,
status,
risk_level,
confirmation_title,
confirmation_body,
request_payload_json,
response_payload_json,
created_at,
expires_at,
decided_at,
decided_by_key_id,
decision_note
from approval_requests
where workspace_id = $1
and id = $2
limit 1",
)
.bind(workspace_id.as_str())
.bind(approval_id.as_str())
.fetch_optional(&self.pool)
.await?;
row.map(map_approval_request_row).transpose()
}
pub async fn decide_approval_request(
&self,
request: DecideApprovalRequest<'_>,
+10 -10
View File
@@ -36,16 +36,16 @@ use crate::{
CreatePlatformApiKeyRequest, CreateSecretRequest, CreateVersionRequest,
CreateWorkspaceRequest, CreateYamlImportJobRequest, DecideApprovalRequest,
DescriptorMetadata, ExpireApprovalRequest, FinishApprovalRequest, FinishImportJobRequest,
ImportJob, ImportJobId, InvitationRecord, InvocationLogRecord, ListInvocationLogsQuery,
MembershipRecord, OperationAgentRef, OperationSampleMetadata, OperationSummary,
OperationUsageSummary, OperationVersionRecord, PlatformApiKeyRecord, PublishAgentRequest,
PublishRequest, PublishedAgentTool, RegistryOperation, RotateSecretRequest,
SaveAgentBindingsRequest, SaveAuthProfileRequest, SaveDescriptorMetadataRequest,
SaveSampleMetadataRequest, SaveWorkspaceUpstreamRequest, SecretRecord, SecretVersionRecord,
SessionRecord, UpdateWorkspaceRequest, UsageAgentBreakdown, UsageOperationBreakdown,
UsageQuery, UsageRollupRecord, UsageSummary, UsageTimelinePoint, WorkspaceMembershipRecord,
WorkspaceRecord, WorkspaceUpstream, YamlImportJob, YamlImportJobCompletion,
YamlImportJobId, YamlImportJobStatus,
ImportJob, ImportJobId, InvitationRecord, InvocationLogRecord, ListApprovalRequestsQuery,
ListInvocationLogsQuery, MembershipRecord, OperationAgentRef, OperationSampleMetadata,
OperationSummary, OperationUsageSummary, OperationVersionRecord, PlatformApiKeyRecord,
PublishAgentRequest, PublishRequest, PublishedAgentTool, RegistryOperation,
RotateSecretRequest, SaveAgentBindingsRequest, SaveAuthProfileRequest,
SaveDescriptorMetadataRequest, SaveSampleMetadataRequest, SaveWorkspaceUpstreamRequest,
SecretRecord, SecretVersionRecord, SessionRecord, UpdateWorkspaceRequest,
UsageAgentBreakdown, UsageOperationBreakdown, UsageQuery, UsageRollupRecord, UsageSummary,
UsageTimelinePoint, WorkspaceMembershipRecord, WorkspaceRecord, WorkspaceUpstream,
YamlImportJob, YamlImportJobCompletion, YamlImportJobId, YamlImportJobStatus,
},
};
@@ -23,11 +23,11 @@ use crank_registry::{
CreateAgentRequest, CreateApprovalRequest, CreateInvocationLogRequest,
CreatePlatformApiKeyRequest, CreateVersionRequest, CreateWorkspaceRequest,
CreateYamlImportJobRequest, DecideApprovalRequest, DescriptorKind, DescriptorMetadata,
FinishApprovalRequest, OperationSampleMetadata, PlatformApiKeyRecord, PostgresRegistry,
PublishAgentRequest, PublishRequest, RegistryError, RegistryOperation, SampleKind,
SaveAuthProfileRequest, SaveDescriptorMetadataRequest, SaveSampleMetadataRequest,
UpdateWorkspaceRequest, WorkspaceRecord, YamlImportJobCompletion, YamlImportJobId,
YamlImportJobStatus,
FinishApprovalRequest, ListApprovalRequestsQuery, OperationSampleMetadata,
PlatformApiKeyRecord, PostgresRegistry, PublishAgentRequest, PublishRequest, RegistryError,
RegistryOperation, SampleKind, SaveAuthProfileRequest, SaveDescriptorMetadataRequest,
SaveSampleMetadataRequest, UpdateWorkspaceRequest, WorkspaceRecord, YamlImportJobCompletion,
YamlImportJobId, YamlImportJobStatus,
};
fn test_workspace_id() -> WorkspaceId {
@@ -531,6 +531,24 @@ async fn manages_approval_request_lifecycle() {
assert_eq!(pending.len(), 1);
assert_eq!(pending[0].approval, approval);
let workspace_approvals = registry
.list_approval_requests(ListApprovalRequestsQuery {
workspace_id: &workspace_id,
status: None,
limit: 10,
})
.await
.unwrap();
assert_eq!(workspace_approvals.len(), 1);
assert_eq!(workspace_approvals[0].approval.id, approval.id);
let fetched = registry
.get_approval_request(&workspace_id, &approval.id)
.await
.unwrap()
.unwrap();
assert_eq!(fetched.approval, approval);
let decided = registry
.decide_approval_request(DecideApprovalRequest {
workspace_id: &workspace_id,
@@ -575,6 +593,20 @@ async fn manages_approval_request_lifecycle() {
Some(json!({"id": "lead_123"}))
);
let completed_by_status = registry
.list_approval_requests(ListApprovalRequestsQuery {
workspace_id: &workspace_id,
status: Some(ApprovalRequestStatus::Completed),
limit: 10,
})
.await
.unwrap();
assert_eq!(completed_by_status.len(), 1);
assert_eq!(
completed_by_status[0].approval.status,
ApprovalRequestStatus::Completed
);
let pending_after_decision = registry
.list_pending_approval_requests_for_agent(&workspace_id, &agent.id)
.await