Remove approval confirmation copy
CI / Rust Checks (push) Successful in 5m32s
CI / UI Checks (push) Successful in 5s
CI / Deployment Manifests (push) Successful in 3s
CI / Frontend E2E (push) Successful in 3m24s
CI / Deploy (push) Successful in 1m33s

This commit is contained in:
github-ops
2026-06-27 07:12:04 +00:00
parent d34c8a73d6
commit 2b2ff92146
14 changed files with 13 additions and 159 deletions
@@ -125,24 +125,6 @@ pub(super) fn validate_approval_policy(
));
}
if policy.confirmation_title.trim().is_empty() {
return Err(ApiError::validation_with_context(
"approval confirmation title is required".to_owned(),
json!({
"field": "execution_config.approval_policy.confirmation_title",
}),
));
}
if policy.confirmation_body_template.trim().is_empty() {
return Err(ApiError::validation_with_context(
"approval confirmation body is required".to_owned(),
json!({
"field": "execution_config.approval_policy.confirmation_body_template",
}),
));
}
Ok(())
}
@@ -291,8 +273,6 @@ mod tests {
config.approval_policy = Some(OperationApprovalPolicy {
required: true,
risk_level: OperationApprovalRiskLevel::Dangerous,
confirmation_title: "Подтвердите действие".to_owned(),
confirmation_body_template: "Выполнить действие?".to_owned(),
ttl_seconds: 300,
show_payload_preview: true,
payload_preview_mode: OperationApprovalPayloadPreviewMode::MaskedJson,
@@ -307,8 +287,6 @@ mod tests {
config.approval_policy = Some(OperationApprovalPolicy {
required: true,
risk_level: OperationApprovalRiskLevel::Dangerous,
confirmation_title: "".to_owned(),
confirmation_body_template: "Выполнить действие?".to_owned(),
ttl_seconds: 0,
show_payload_preview: true,
payload_preview_mode: OperationApprovalPayloadPreviewMode::MaskedJson,
@@ -23,8 +23,6 @@ async fn approval_key_lists_and_decides_pending_requests() {
operation_version: 1,
status: ApprovalRequestStatus::Pending,
risk_level: OperationApprovalRiskLevel::Dangerous,
confirmation_title: "Подтвердите создание лида".to_owned(),
confirmation_body: "Проверьте email перед отправкой в CRM.".to_owned(),
request_payload: json!({"email": "ada@example.com"}),
response_payload: None,
created_at: OffsetDateTime::now_utc(),
@@ -79,8 +77,8 @@ async fn approval_key_lists_and_decides_pending_requests() {
let pending_body = pending.json::<Value>().await.unwrap();
assert_eq!(pending_body["items"].as_array().unwrap().len(), 1);
assert_eq!(
pending_body["items"][0]["approval"]["confirmation_title"],
"Подтвердите создание лида"
pending_body["items"][0]["approval"]["request_payload"],
json!({"email": "ada@example.com"})
);
let approve_url = format!(
@@ -196,8 +194,6 @@ async fn approval_key_denies_without_executing_upstream() {
operation_version: 1,
status: ApprovalRequestStatus::Pending,
risk_level: OperationApprovalRiskLevel::Dangerous,
confirmation_title: "Подтвердите создание лида".to_owned(),
confirmation_body: "Проверьте email перед отправкой в CRM.".to_owned(),
request_payload: json!({"email": "deny@example.com"}),
response_payload: None,
created_at: OffsetDateTime::now_utc(),
@@ -295,8 +291,6 @@ async fn approval_key_expires_without_executing_upstream() {
operation_version: 1,
status: ApprovalRequestStatus::Pending,
risk_level: OperationApprovalRiskLevel::Dangerous,
confirmation_title: "Подтвердите создание лида".to_owned(),
confirmation_body: "Проверьте email перед отправкой в CRM.".to_owned(),
request_payload: json!({"email": "expired@example.com"}),
response_payload: None,
created_at: OffsetDateTime::now_utc() - time::Duration::minutes(10),
@@ -366,8 +360,6 @@ async fn tool_call_with_approval_policy_creates_pending_request() {
operation.execution_config.approval_policy = Some(OperationApprovalPolicy {
required: true,
risk_level: OperationApprovalRiskLevel::Dangerous,
confirmation_title: "Подтвердите создание лида".to_owned(),
confirmation_body_template: "Проверьте email перед отправкой в CRM.".to_owned(),
ttl_seconds: 300,
show_payload_preview: true,
payload_preview_mode: OperationApprovalPayloadPreviewMode::MaskedJson,
-31
View File
@@ -1138,37 +1138,6 @@
justify-content: center;
}
.approval-preview-card {
display: grid;
gap: 6px;
padding: 14px 16px;
border: 1px solid rgba(47, 129, 247, 0.28);
border-radius: 10px;
background:
linear-gradient(135deg, rgba(47, 129, 247, 0.12), rgba(35, 134, 54, 0.06)),
var(--bg-overlay);
}
.approval-preview-eyebrow {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--accent);
}
.approval-preview-title {
font-size: 14px;
font-weight: 700;
color: var(--text-primary);
}
.approval-preview-body {
font-size: 12.5px;
line-height: 1.6;
color: var(--text-secondary);
}
/* ══════════════════════════════════════════════════
BOTTOM ACTION BAR — frosted dark glass
══════════════════════════════════════════════════ */
-18
View File
@@ -123,18 +123,6 @@
</select>
</div>
<div class="form-group">
<label class="form-label" for="approval-title" data-i18n="wizard.approval.confirmation_title">Заголовок подтверждения</label>
<input id="approval-title" class="form-input" type="text" autocomplete="off" placeholder="Подтвердите выполнение операции">
<div class="form-hint" data-i18n="wizard.approval.confirmation_title_hint">Этот текст увидит внешний интерфейс подтверждения.</div>
</div>
<div class="form-group">
<label class="form-label" for="approval-body" data-i18n="wizard.approval.confirmation_body">Описание для пользователя</label>
<textarea id="approval-body" class="form-textarea" rows="4" placeholder="Проверьте параметры операции и подтвердите выполнение."></textarea>
<div class="form-hint" data-i18n="wizard.approval.confirmation_body_hint">Коротко объясните, что произойдет после подтверждения.</div>
</div>
<div class="form-row">
<label class="checkbox-pill approval-preview-pill">
<input id="approval-show-payload-preview" type="checkbox" checked>
@@ -148,12 +136,6 @@
</select>
</div>
</div>
<div class="approval-preview-card">
<div class="approval-preview-eyebrow" data-i18n="wizard.approval.preview_label">Предпросмотр для интерфейса подтверждения</div>
<div class="approval-preview-title" id="approval-preview-title">Подтвердите выполнение операции</div>
<div class="approval-preview-body" id="approval-preview-body">Проверьте параметры операции и подтвердите выполнение.</div>
</div>
</div>
</div>
</div>
-14
View File
@@ -599,17 +599,10 @@ var TRANSLATIONS = {
'wizard.approval.required_label': 'Require confirmation before execution',
'wizard.approval.required_desc': 'The MCP client receives a pending request, and the action runs only after confirmation through a separate approval endpoint.',
'wizard.approval.ttl': 'How long to wait for confirmation',
'wizard.approval.confirmation_title': 'Confirmation title',
'wizard.approval.confirmation_title_hint': 'This text will be shown by the external confirmation interface.',
'wizard.approval.confirmation_body': 'Description for the user',
'wizard.approval.confirmation_body_hint': 'Explain briefly what will happen after confirmation.',
'wizard.approval.show_payload': 'Show request parameters',
'wizard.approval.payload_mode': 'How to show parameters',
'wizard.approval.payload.summary': 'Short summary',
'wizard.approval.payload.masked_json': 'JSON with masked secrets',
'wizard.approval.preview_label': 'Preview for confirmation UI',
'wizard.approval.default_title': 'Confirm operation execution',
'wizard.approval.default_body': 'Review operation parameters and confirm execution.',
'wizard.step5.security_level_title': 'Operation security',
'wizard.step5.community_security_note': '',
'wizard.step5.live_title': 'Check and publish',
@@ -1461,17 +1454,10 @@ var TRANSLATIONS = {
'wizard.approval.required_label': 'Требовать подтверждение перед выполнением',
'wizard.approval.required_desc': 'MCP клиент получит ожидающий запрос, а действие выполнится только после подтверждения через отдельный эндпоинт подтверждения.',
'wizard.approval.ttl': 'Сколько ждать подтверждение',
'wizard.approval.confirmation_title': 'Заголовок подтверждения',
'wizard.approval.confirmation_title_hint': 'Этот текст увидит внешний интерфейс подтверждения.',
'wizard.approval.confirmation_body': 'Описание для пользователя',
'wizard.approval.confirmation_body_hint': 'Коротко объясните, что произойдет после подтверждения.',
'wizard.approval.show_payload': 'Показывать параметры запроса',
'wizard.approval.payload_mode': 'Как показывать параметры',
'wizard.approval.payload.summary': 'Краткое описание',
'wizard.approval.payload.masked_json': 'JSON с маскированием секретов',
'wizard.approval.preview_label': 'Предпросмотр для интерфейса подтверждения',
'wizard.approval.default_title': 'Подтвердите выполнение операции',
'wizard.approval.default_body': 'Проверьте параметры операции и подтвердите выполнение.',
'wizard.step5.security_level_title': 'Защита операции',
'wizard.step5.community_security_note': '',
'wizard.step5.live_title': 'Проверка и публикация',
+1 -7
View File
@@ -138,8 +138,6 @@ document.addEventListener('DOMContentLoaded', function () {
operationVersion: approval.operation_version,
status: approval.status,
riskLevel: approval.risk_level,
title: approval.confirmation_title,
body: approval.confirmation_body,
requestPayload: approval.request_payload,
responsePayload: approval.response_payload,
createdAt: approval.created_at,
@@ -186,7 +184,7 @@ document.addEventListener('DOMContentLoaded', function () {
var header = element('div', 'approval-item-header');
var titleWrap = element('div', 'approval-item-title-wrap');
titleWrap.appendChild(element('div', 'approval-item-title', item.title || tKey('approvals.untitled')));
titleWrap.appendChild(element('div', 'approval-item-title', tKey('approvals.untitled') + ' ' + item.id));
var meta = element('div', 'approval-item-meta');
meta.textContent = [
@@ -200,10 +198,6 @@ document.addEventListener('DOMContentLoaded', function () {
header.appendChild(badge);
card.appendChild(header);
if (item.body) {
card.appendChild(element('p', 'approval-item-body', item.body));
}
var timing = element('div', 'approval-timing');
timing.textContent = item.status === 'pending'
? tKey('approvals.expires_at') + ': ' + formatDateTime(item.expiresAt)
-14
View File
@@ -41,14 +41,9 @@ function normalizeApprovalTtlSeconds(value) {
function buildApprovalPolicy() {
if (!checkedValue('approval-required')) return null;
var title = textValue('approval-title') || tKey('wizard.approval.default_title');
var body = textValue('approval-body') || tKey('wizard.approval.default_body');
return {
required: true,
risk_level: 'normal',
confirmation_title: title,
confirmation_body_template: body,
ttl_seconds: normalizeApprovalTtlSeconds(textValue('approval-ttl-seconds')),
show_payload_preview: checkedValue('approval-show-payload-preview'),
payload_preview_mode: textValue('approval-payload-preview-mode') || 'summary',
@@ -192,8 +187,6 @@ function setApprovalPolicyEditor(policy) {
var required = document.getElementById('approval-required');
if (required) required.checked = enabled;
setValue('approval-ttl-seconds', policy && policy.ttl_seconds ? String(policy.ttl_seconds) : '300');
setValue('approval-title', policy && policy.confirmation_title ? policy.confirmation_title : tKey('wizard.approval.default_title'));
setValue('approval-body', policy && policy.confirmation_body_template ? policy.confirmation_body_template : tKey('wizard.approval.default_body'));
var showPayload = document.getElementById('approval-show-payload-preview');
if (showPayload) {
showPayload.checked = !policy || policy.show_payload_preview !== false;
@@ -208,19 +201,12 @@ function updateApprovalPolicyUi() {
var fields = document.getElementById('approval-config-fields');
if (toggle) toggle.classList.toggle('on', enabled);
if (fields) fields.hidden = !enabled;
var title = textValue('approval-title') || tKey('wizard.approval.default_title');
var body = textValue('approval-body') || tKey('wizard.approval.default_body');
setTextContent('approval-preview-title', title);
setTextContent('approval-preview-body', body);
}
function bindApprovalPolicyControls() {
[
'approval-required',
'approval-ttl-seconds',
'approval-title',
'approval-body',
'approval-show-payload-preview',
'approval-payload-preview-mode',
].forEach(function(id) {
-8
View File
@@ -553,8 +553,6 @@ test('wizard shows agent-facing MCP preview from current draft fields', async ({
approval_policy: {
required: true,
risk_level: 'financial',
confirmation_title: 'Подтвердите обмен валюты',
confirmation_body_template: 'Проверьте валюты и подтвердите выполнение операции.',
ttl_seconds: 180,
show_payload_preview: true,
payload_preview_mode: 'masked_json',
@@ -777,8 +775,6 @@ test('wizard edit mode preserves explicit request mapping targets on save', asyn
approval_policy: {
required: true,
risk_level: 'financial',
confirmation_title: 'Подтвердите обмен валюты',
confirmation_body_template: 'Проверьте валюты и подтвердите выполнение операции.',
ttl_seconds: 180,
show_payload_preview: true,
payload_preview_mode: 'masked_json',
@@ -833,8 +829,6 @@ test('wizard edit mode preserves explicit request mapping targets on save', asyn
await expect(page.locator('#approval-config-fields')).toBeVisible();
await expect(page.locator('#approval-risk-level')).toHaveCount(0);
await expect(page.locator('#approval-ttl-seconds')).toHaveValue('180');
await expect(page.locator('#approval-title')).toHaveValue('Подтвердите обмен валюты');
await expect(page.locator('#approval-body')).toHaveValue('Проверьте валюты и подтвердите выполнение операции.');
await expect(page.locator('#approval-payload-preview-mode')).toHaveValue('masked_json');
await page.locator('.btn-save-draft').click();
await expect.poll(() => updatePayload).not.toBeNull();
@@ -864,8 +858,6 @@ test('wizard edit mode preserves explicit request mapping targets on save', asyn
approval_policy: {
required: true,
risk_level: 'normal',
confirmation_title: 'Подтвердите обмен валюты',
confirmation_body_template: 'Проверьте валюты и подтвердите выполнение операции.',
ttl_seconds: 180,
show_payload_preview: true,
payload_preview_mode: 'masked_json',
-4
View File
@@ -1086,8 +1086,6 @@ async fn maybe_create_pending_approval(
},
"expires_at": expires_at,
"risk_level": policy.risk_level,
"confirmation_title": policy.confirmation_title,
"confirmation_body": policy.confirmation_body_template,
"payload_preview": if policy.show_payload_preview {
arguments.clone()
} else {
@@ -1102,8 +1100,6 @@ async fn maybe_create_pending_approval(
operation_version: tool.operation.version,
status: ApprovalRequestStatus::Pending,
risk_level: policy.risk_level,
confirmation_title: policy.confirmation_title.clone(),
confirmation_body: policy.confirmation_body_template.clone(),
request_payload: arguments.clone(),
response_payload: None,
created_at: now,
-2
View File
@@ -26,8 +26,6 @@ pub struct ApprovalRequest {
pub operation_version: u32,
pub status: ApprovalRequestStatus,
pub risk_level: OperationApprovalRiskLevel,
pub confirmation_title: String,
pub confirmation_body: String,
pub request_payload: Value,
pub response_payload: Option<Value>,
#[serde(with = "time::serde::rfc3339")]
-2
View File
@@ -125,8 +125,6 @@ pub enum OperationApprovalPayloadPreviewMode {
pub struct OperationApprovalPolicy {
pub required: bool,
pub risk_level: OperationApprovalRiskLevel,
pub confirmation_title: String,
pub confirmation_body_template: String,
pub ttl_seconds: u32,
pub show_payload_preview: bool,
pub payload_preview_mode: OperationApprovalPayloadPreviewMode,
+7 -2
View File
@@ -568,8 +568,6 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
operation_version integer not null,
status text not null,
risk_level text not null,
confirmation_title text not null,
confirmation_body text not null,
request_payload_json jsonb not null,
response_payload_json jsonb null,
created_at timestamptz not null,
@@ -581,6 +579,13 @@ pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
)
.execute(pool)
.await?;
query("alter table approval_requests drop column if exists confirmation_title")
.execute(pool)
.await?;
query("alter table approval_requests drop column if exists confirmation_body")
.execute(pool)
.await?;
query(
"create index if not exists approval_requests_agent_status_idx
on approval_requests(workspace_id, agent_id, status, expires_at)",
+3 -23
View File
@@ -14,8 +14,6 @@ impl PostgresRegistry {
operation_version,
status,
risk_level,
confirmation_title,
confirmation_body,
request_payload_json,
response_payload_json,
created_at,
@@ -24,9 +22,9 @@ impl PostgresRegistry {
decided_by_key_id,
decision_note
) values (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10,
$11, $12::timestamptz, $13::timestamptz, $14::timestamptz,
$15, $16
$1, $2, $3, $4, $5, $6, $7, $8,
$9, $10::timestamptz, $11::timestamptz, $12::timestamptz,
$13, $14
)",
)
.bind(request.approval.id.as_str())
@@ -42,8 +40,6 @@ impl PostgresRegistry {
&request.approval.risk_level,
"approval_risk_level",
)?)
.bind(&request.approval.confirmation_title)
.bind(&request.approval.confirmation_body)
.bind(Json(&request.approval.request_payload))
.bind(request.approval.response_payload.as_ref().map(Json))
.bind(request.approval.created_at)
@@ -77,8 +73,6 @@ impl PostgresRegistry {
operation_version,
status,
risk_level,
confirmation_title,
confirmation_body,
request_payload_json,
response_payload_json,
created_at,
@@ -116,8 +110,6 @@ impl PostgresRegistry {
operation_version,
status,
risk_level,
confirmation_title,
confirmation_body,
request_payload_json,
response_payload_json,
created_at,
@@ -157,8 +149,6 @@ impl PostgresRegistry {
operation_version,
status,
risk_level,
confirmation_title,
confirmation_body,
request_payload_json,
response_payload_json,
created_at,
@@ -195,8 +185,6 @@ impl PostgresRegistry {
operation_version,
status,
risk_level,
confirmation_title,
confirmation_body,
request_payload_json,
response_payload_json,
created_at,
@@ -241,8 +229,6 @@ impl PostgresRegistry {
operation_version,
status,
risk_level,
confirmation_title,
confirmation_body,
request_payload_json,
response_payload_json,
created_at,
@@ -286,8 +272,6 @@ impl PostgresRegistry {
operation_version,
status,
risk_level,
confirmation_title,
confirmation_body,
request_payload_json,
response_payload_json,
created_at,
@@ -328,8 +312,6 @@ impl PostgresRegistry {
operation_version,
status,
risk_level,
confirmation_title,
confirmation_body,
request_payload_json,
response_payload_json,
created_at,
@@ -362,8 +344,6 @@ fn map_approval_request_row(row: PgRow) -> Result<ApprovalRequestRecord, Registr
&row.get::<String, _>("risk_level"),
"approval_risk_level",
)?,
confirmation_title: row.get("confirmation_title"),
confirmation_body: row.get("confirmation_body"),
request_payload: row.get::<Value, _>("request_payload_json"),
response_payload: row.get::<Option<Value>, _>("response_payload_json"),
created_at: row.get("created_at"),
@@ -487,8 +487,6 @@ async fn manages_approval_request_lifecycle() {
operation_version: 1,
status: ApprovalRequestStatus::Pending,
risk_level: OperationApprovalRiskLevel::Dangerous,
confirmation_title: "Confirm action".to_owned(),
confirmation_body: "Check payload before running.".to_owned(),
request_payload: json!({"amount": 100}),
response_payload: None,
created_at: timestamp("2026-03-25T12:01:00Z"),